Business Rules - rogers-group-inc/polaris GitHub Wiki

Business rules

Polaris carries 119 rule numbers, 1 to 119: 117 rules in force plus rules 78a, 78b and 80a, with 81 and 88 not in use. Each one records a decision and the reasoning behind it. The reasoning is the point — a great deal of Polaris's behaviour is a considered rule rather than an accident, and this is where the reasons live.

Rule numbers are a stable citation key. Commits, code comments and the rest of this wiki cite "rule 23". They are never renumbered; a retired rule is retired in place.

The full narrative for every rule — the failure mode it prevents and the alternatives considered — lives in the repository under .claude/skills/polaris-business-rules/. This page is the index, in the rules' own terms.


Rules 1–11 — the IPAM floor

Rule 1

No overlapping networks within one block. Enforced at the service layer under a per-block advisory lock, and backed by a unique index. A create that bypasses the lock re-opens the race in rule 20a.

Rule 2

A network must be contained within its parent block. A new network is placed in the most specific block that contains it — + Add Network never asks for one — and is refused if no block contains it.

Rule 3

No duplicate IP reservations — one active reservation per address per network, backed by a unique index.

Rule 4

Block and network deletion are protected. A network delete is refused (409) while any active reservation exists inside it — except the firewall's own interface IP on the network, which never counts and is deleted with it. An administrator (a role with full write on both Users and Roles) is warned and may delete the network anyway; its reservations go with it, nothing is removed from any FortiGate, and the Event is logged as a warning. A block delete is refused (409) while the block contains any network at all — move each one to another block (Move to block… on the Networks tab), archive it or delete it first. (Archiving is deliberately exempt — see rule 41.)

Rule 5

CIDRs are normalised on write. Host bits are zeroed: 10.1.1.5/24 stores as 10.1.1.0/24.

Rule 6

Every discovered reservation carries a sourceType. Manual entries default to manual.

Rule 7

Conflict detection. A discovery value differing from an existing manual reservation creates a Conflict rather than overwriting.

Rule 8

Event archival. Events older than the configured retention (default 7 days, set in the Events page's Settings) are archived and then pruned; syslog (RFC 5424 or 3164) and SFTP/SCP archival are configurable.

Rule 9

acquiredAt ≤ lastSeen, clamped on every write and repaired at boot.

Rule 10

Four statuses cannot be monitored — decommissioned, disabled, storage, quarantined. Enforced centrally in both directions, so no write path can stage monitoring onto one.

quarantined is on the list because a quarantined device is isolated at the gate and every probe fails by design — monitoring it would raise an outage alert storm about the isolation working. Because quarantine is reversible the flag is parked and restored on release, so a released device does not come back with nobody watching it.

maintenance is deliberately not on the list. The two operator-facing paths refuse with a reason rather than leaning on the silent clamp — a form that saves and comes back unticked reads as a bug.

Rule 11

DNS-resolved reservations. Any asset whose primary IP falls in a known, non-deprecated network with no active reservation gets one auto-created. IPv4 only, never pushed to a gate, and it never raises a conflict — it defers silently to anything authoritative.


Rules 12–29

Rule 12

lastSeen means verified network presence. Written through one function that never regresses it and stamps provenance. Polling is authoritative for monitored assets — the discovery-origin sources are refused so they cannot advance presence past what polling established. A failed probe writes nothing.

Rule 13

SD-WAN monitoring is opt-in, read-only, FortiOS-only. It never writes to the device. Where FortiOS will not expose the runtime selected route, the value is inferred and labelled as such.

Rule 14

Description sync is opt-in and Polaris-primary. A non-empty Polaris value always wins; an empty Polaris field adopts the device value. No conflict state exists. It is switched on per device class — FortiGates, FortiSwitches and FortiAPs each have their own toggle.

Rule 15

Location codes ride device descriptions; notes are operator-only. The asset description's codes win exclusively when it carries any — no per-key fall-through — and discovery never writes notes on an existing asset.

Rule 16

Maintenance windows pause everything, and status flips are scheduler-managed. An alert already open when the window starts stays open — it is silenced (no new alerts, escalation and reminders paused) and, once polling resumes, resolves normally through its own automation if the device is healthy — or sooner, if a reading taken during the window (an agent push, a Poll Now) shows it healthy. (a) Down alerts are the exception: being down is what planned downtime looks like, so an open device down alert (an automation on "monitor status is down") is cleared when the device enters maintenance, or when it is silenced behind a parent that has; if the device is still down after the window, a new alert fires. No other alert is cleared by a window; only a genuine outage upstream clears a silenced child's other alerts (rule 38). A day carries a list of hour ranges, and each range is its own occurrence with its own start. See Maintenance windows.

Rule 17

ARP presence is evidence for stale reservations; the sweep is opt-in. MAC match is required, matching is scoped per (gate, IP), and absence of an ARP entry is never negative evidence. The cache-priming sweep is off by default because it is IDS-visible.

Rule 18

Automation precedence is same-trigger, most-specific-wins. Higher scope rank carves out the devices it covers from every lower-ranked automation; same-rank ties both fire. monitorStatus is keyed by operator and value, not by device filter — it is one per-asset column, so every == down rule must be able to carve the others out. See Automations.

Rule 19

Severity bands escalate one alert by value, each on its own clock. Severities must strictly increase; thresholds need not be monotonic — the most-severe met tier wins. Holds are counted in readings, not seconds.

Rule 20

Three operability invariants. (a) Network writes serialise per block. (b) Secrets are encrypted at rest — sealing per model, opening all-models; raw SQL bypasses it. (c) A backup you cannot restore is not a backup — the path is streamed end to end, restore is wrapped in TimescaleDB's pre/post-restore calls, and a failed pre-update backup aborts the update by default.

Rule 21

SSH host-key verification is on unless a credential turns it off, and fails closed. No pin stores and accepts; a match accepts; a changed key refuses the connection. An internal error refuses rather than silently skipping. See rule 113 for the same check on WinRM and firmware certificates.

Rule 22

The Sources column is location || learnedLocation, and the order is operator-set. It feeds the projection, not just rendering. Invariant: a source blob may never be built from the projection's own output — doing so launders one source's value (an AD OU path) into another's field (a FortiGate-sourced one) and accumulates a prefix per cycle.

Rule 23

Who owns an IP and how the gate hands it out are two facts, not one. sourceType answers ownership; dhcpBinding (null / lease / reservation) answers service. The binding is written without ever flipping the source type. A managed device that goes away gives its address back — on decommission, never on "no longer discovered".

Rule 24

Alert on the device's own alarm bit before inventing a threshold for it. The device knows its own per-model limits. A source publishing no alarm bit must never have its absence mapped to 0 — and an absent reading must not be read as an absent dimension either, or a dropped collection becomes indistinguishable from a port that has no such feature.

Rule 25

An alert you cannot acknowledge from where you read it is not acknowledgeable, and the acknowledger is a session. One URL for email and push, identity from the session behind it. Loading is a GET and acknowledging is a POST, so a mail gateway prefetching links cannot acknowledge anything. An alert that is over carries no button at all. And one notify action sends one email: everyone it names is on the same To line, so each reader can see who else is already on it. Nothing about the reader splits that message — not their timezone (times are the Polaris server's, and the footer names the zone) and not their permissions (a reader who cannot acknowledge gets the button and is refused, with a reason, on the acknowledge page). An alert outside a reader's regions reads as not found rather than forbidden — except to an administrator, whom region scope never narrows on alerts. See Actions and region scope.

Rule 26

A generated MAC is a placeholder until the network proves otherwise. The placeholder prefix is the only marker — one visible on the gate's own table. Adoption is double-gated, works on ARP and device-inventory evidence only, and is not retroactive in either direction.

Rule 27

A logo is picked by theme family; a name is text only when the picture does not already say it. Painting the wrong variant makes the logo disappear.

Rule 28

The Windows build is the authority; the product name is not. ProductName reads "Windows 10 Pro" on every Windows 11 client, so no projection priority can fix it. The family comes from a build threshold; an unlisted build keeps its raw version. Windows Server is excluded entirely. Not retroactive.

Rule 29

A miss taken while the device is DOWN is the outage, not the link; everything else counts. The packet-loss ratio is plainly failed/total over the window, it counts packets not rows where it can, only an answering device reports loss, and a reading at or above the rule's ceiling is not a reading. Every maximal run of failures that reached down is dropped whole, onset included.

The chart and the alert can legitimately disagree — the chart counts every probe because a picture of a window has to be continuous. See Triggers.

The same holds for every other reading the device reports. When a device goes down, its open metric and device-state alerts clear as superseded, with no "resolved" message, and the asset-down alert speaks for the outage. See Triggers.


Rules 30–48

Rule 30

Confirmation is the configured cadence's job; ICMP only fills in the loss ratio. The failure counter is a leaky bucket with a ceiling. An answered probe is recovering whatever the level; the level decides what a miss means. Recovery costs exactly the cap, however long the outage ran. A skip is not a miss, and a re-queued job is not a second reading. See Monitor states.

Rule 31

The login page is the way back in, so restricting it is opt-in and must refuse to lock you out. Both halves or neither; the page is dropped and the API answers the same generic 401; SSO is never gated; it fails open on a read error; the save refuses a scope excluding the caller.

Rule 32

A reset condition answers "what has to become true again", so it starts as the trigger inverted — and it resolves where the alert lives. While firing, the tree is the sole recovery authority. Resolution is dimension-first: a reset condition on the same kind of component clears that component's alert alone, and only a condition on something device-wide (CPU, memory, monitor status) clears them together — one healthy port never clears another port's alert. A reset condition also watches the same components the trigger does, including the unpinned PoE ports a fault condition covers (Rule 57). Reset leaves inherit the trigger's window. Event and change triggers get a counterpart Event instead — and a signal Polaris writes under one action for both directions is split so it has one (capacity.severity_recovered, platform.lifecycle_recovered), written only on a landing back at healthy, never on a partial recovery. The direction a trigger fires on is an editable detail condition on the trigger step, not a hidden setting.

Rule 33

A device that answers is not a device that works — and the check belongs to the vendor, not to a login. The HTTP check is a manufacturer widget, not a polling method. The body match is load-bearing, redirects are never followed, and it never moves monitorStatus.

Rule 34

An active scan finds things; a separate grant adds them. Scanning and adopting are separate grants, chained at the route. Opt-in and IDS-visible: you run a scan when you mean to. Adoption is new-addresses-only. A Discovery is private or shared: who can see and run it is its visibility, who can edit it is its owner (or anyone with Full Read-Write on Network Discovery). See Network Discovery.

Rule 35

The GAL is a mirror, not a source of truth — and Polaris only deletes what it wrote. Provenance decides deletion; a Polaris user or manual contact wins; an empty or catastrophically shrunken read never wipes; the run Event carries counts only.

Rule 36

The automation decides what "down" means; a device no automation covers is never judged. A device covered by no down automation reads passive — still polled, still charted, no verdict. The same automation owns the way back up, and decides what colour Down is.

Rule 37

An automation only fires about a device Polaris is actually polling. One gate, every trigger path: monitored and not suppressed. Un-monitoring a device clears its live alerts. A deleted asset still fires.

Rule 38

A device behind a dark parent is not accused, and is not released on one packet. (a) Release is asymmetric — entering needs every parent confirmed down, leaving needs a parent genuinely back (up / unknown / passive only; warning holds). (b) A miss the upstream explains is grey, not red. (c) A suppressed device keeps its own count at the normal probe interval, so when its parent reaches up, every device below whose own count is already up leaves Dep. Down in the same moment, all the way down the chain.

Rule 39

How a person wants to be reached is theirs, not the automation's. The notification preference is an account setting, and every client reconciles its own subscription to it at boot. That reconcile never prompts, so a browser that has never been asked for notification permission is asked once instead — and never on iOS outside the installed app. It is consulted only when the action group offers both methods, only accounts are filtered, and a preference never deletes an alert.

Rule 40

Two assets on one address is a conflict; one asset on a stale address is not. Ten lettered clauses, (a) to (j) — see Conflict resolution. The short version: only network-present assets, only current claims, two devices not two rows, one card per address, two verbs instead of accept, and one claimant must be equipment somebody addressed on purpose or the address itself must be deliberate with disjoint reporting sources.

The sweep is not the only trigger. Creating an asset with an IP, or changing an asset's IP in the edit form, re-checks that address on the spot: the form first asks whether another network-present asset already records it and, if so, offers Save & submit for conflict review — and, to an operator with Assets full read-write, Save & review merge — before anything is written. The save then raises the Duplicate IP conflict immediately instead of on the sweep's next tick. A third reason a pair qualifies: an address an operator typed counts as deliberately assigned whatever the device types are, so two workstations one of which you addressed by hand is a conflict. Merging assets requires Assets full read-write everywhere — it edits one record and deletes another. See Conflict Resolution.

An offline device can be cleared. When one claimant is off and its next address is unknown, Clear on its row blanks the address instead of pinning a new one; discovery fills the blank the first time it reports the device on a different address, and won't put the old one back while the other device still holds it.

Rule 41

A subnet dies with its FortiGate, and the chassis — not the name — says which gate that is. A name cannot tell a rename from a replacement. Tri-state in both directions, compared against the cluster's whole serial set, and the conflict is additive. A retired network moves to the archive rather than going deprecated, because a deprecated row's CIDR becomes unrecordable rather than reusable.

One swap is one decision. A replaced gate raises a conflict for each network it serves, but the Conflicts page shows them as one card with one Adopt for every network, and the nav badge counts the swap once. Adopting can also merge the old gate's asset into the new one. And the old gate's asset is decommissioned by discovery when FortiManager lists the same name with a different serial, so a swap that keeps the name does not leave the old record active. Its switches and APs are not touched, because they belong to the new gate.

Rule 42

Some address space is not one network, and the way to say so is to exclude it. Global, CIDR-identified and frozen after create, one-directional containment, enforced at the single creation seam, taken space to an allocator rather than a refusal, and it destroys nothing.

Rule 43

A grant is only as narrow as the act it names. Deploying the agent is assets:fullwrite, not write. assetsProbe is a read-only key. credentials carries the ownership dimension, testing a stored row included.

Across the whole catalogue, a level exists only where it grants something the level below it does not. A rung no button or route asks for is not offered, because picking it and picking the one below it would be the same grant. A level also sits where the act belongs: downloading a backup is the whole database leaving the host, so it is not a read, and the asset auto-decommission thresholds sit on monitor settings rather than the audit-log key. See Users, roles and permissions.

A level can also be divided in the wrong place. The identity providers, passkey policy and password policy sit on a key of their own, Authentication, rather than under Server Settings — System, because repointing every login at a different provider must not be a lesser grant than changing the logo, and each job can be delegated without the other. See Who may change how people log in.

The opposite mistake is one act split across two keys. An alias decides which manufacturer profile a device gets, so the manufacturer alias map could never really be granted apart from the profiles; it is part of Manufacturer Profiles rather than a key of its own.

Rule 44

A quiet window holds the reminder, never skips it — and the reminder that follows says how long. Held, never skipped. The hold is closed by the send, not by the window ending. This is the mechanics inside rule 92: the windows belong to the automation (or to a global quiet time), and quiet reaches the escalation tiers too — and, when the quiet time holds everything, the first alert.

Rule 45

An address places a device only through the gate that owns it, and only a device nothing else can place. MAC-less assets only, one chain, four refusals. The derived MAC is adopted — refused when another asset already carries it, and refused for both when two candidates resolve to the same one. Stamps are never cleared: absence of evidence is not a move.

Rule 46

A device filter on an event automation filters the event's subject. An event naming no asset cannot satisfy a device filter, so a filtered automation never fires on one — an unconstrained one still does. "Unconstrained" is its own question, asked first.

Rule 47

A PostgreSQL client is chosen by the server's major and verified, never spawned by bare name. pg_dump refuses a server newer than itself. Presence is not compatibility. A mismatch refuses the backup with the sentence the operator needs — both versions, the path, the fix — as the error itself, never behind "see the server log".

Rule 48

Nobody hands out authority they do not hold. Admin-equivalence cannot be minted by a caller who lacks it, at any of the four sites. The same bar holds on the other side of the act: resetting an administrator's password, removing their two-factor methods or passkeys, deleting them or changing their role, and creating an API token bound to an admin-equivalent role, all need the caller to be admin-equivalent too — and so does taking admin-equivalence away from a role, which may never leave the install without an administrator. Deleting an account ends its ownership: the networks, reservations, credentials and contacts it created become owned by no account (shown as deleted:<name>), so a later account with the same name inherits nothing. Accounts and roles below that tier stay within reach of Users Read-Write and API Tokens Read-Write. Not four-eyes; the mirror of the last-admin guard. A request with no role snapshot fails closed.


Rules 49–64

Rule 49

An upgrade never refuses for want of a credential it can find itself, and never records one it has not proved. An unresolvable stored id counts as absent; an explicit one that fails is an error. The transport follows the credential's type. Adoption happens only on success — reaching the host is the proof.

Rule 50

A response the app did not write is a response with the app's headers missing. An unmatched route is answered, never dropped to the framework's handler, which would replace the CSP with one dropping frame-ancestors and form-action and echo the request back. The 404 message is a constant. Under /api/v1 an anonymous caller gets 401, not 404.

Rule 51

A DATABASE_URL is a driver URL; its sslmode is translated into libpq's vocabulary, never copied. no-verify maps to require; an unrecognised value is refused rather than dropped, because omitting it silently downgrades TLS to opportunistic. Copying it unchanged breaks every backup path on an install created through the wizard with self-signed certificates ticked, behind one generic message.

Rule 52

TimescaleDB is part of the install, not a tuning option. Its absence is a broken install from the first byte, not a problem that begins at a size threshold — so the capacity warning has no size gate. Every install path provisions it and errors out rather than warning.

Rule 53

A device a run could not read keeps its old data, so it is named — never folded in with one the run skipped. Offline and unread are different states and no surface may sum them. The projection's null-guards mean an absent read never blanks a good value — and therefore never corrects one either. "It is being monitored fine" is not evidence that anything has read it.

Rule 54

A region tag dies when its name is retired, and only then. Both halves — no region answers to the name, and Polaris recorded retiring it. Stripping every region: tag matching no region is the one thing this must not do, because manual attachments survive every reconciler.

Rule 55

IPAM is the last source consulted for a device's upstream gate — it may add a parent, never move one, and every surface says which answer it got. (The write side, where an address actually places a device, is rule 45.) It can only ever add a parent, never move one, so the failure mode is a missed alert and never a false one. An inference has no moment, so the row prints no timestamp.

Rule 56

What ignoring an alert costs is answered where the thing that costs it lives: the note by severity, the reminder by action. Both tests are presence, never truthiness — an explicit false is an answer, an absent key inherits.

Rule 57

A sub-asset alerts only if the operator pinned it — the pin IS the statement of what may alert. The pin is a gate, never a side effect of retention: every stream writes samples for unpinned members too. There is no opt-out; un-pinning is how alerting stops. One exception: a PoE condition written as Interface PoE status is fault (or other-fault) alerts on every PoE-capable port, pinned or not. A port with nothing plugged in reports searching and a port you switched PoE off on reports disabled, so fault can only mean the switch detected a powered device and failed to power it — which is the one PoE failure you cannot find another way, because the access point or camera on the far end never comes up at all. Every other PoE comparison, and every other interface condition, stays pinned-only.

Rule 58

A region tag naming no region strands the whole ranking, so level routing abstains rather than promoting the container. Dropping the leaf promotes its container to L1 — the automation pages the division while the site's own people hear nothing, and it is undetectable from every surface an operator has. Abstention is scoped to the level arm alone.

Rule 59

The controller's view of its own link is a second opinion, and an unreadable controller has no view at all. An unreadable controller writes nothing; answered-but-absent is unknown, never down; it never touches monitorStatus. A null produces no reading at all.

Rule 60

A footer that tells the reader who else knows must never name a Bcc. A blind copy that appears in a footer every recipient reads has stopped being blind. The invariant is not "the renderer filters Bcc out" but "the renderer is never handed one". The footer names the audience of the send — one dispatch of the automation's actions — rather than of the whole alert, so a reminder never names someone who is only on an escalation tier.

Rule 61

Changing a credential ends every other session on it, and rotating your own must carry the CSRF token across. The point of a new password is the revocation. The caller's own session is rotated but kept — and the CSRF token must survive that, or the still-open page 403s on its next write with nothing thrown anywhere.

Rule 62

An install is identified by something it persists, never by the name the runtime handed the process. In a container os.hostname() is the container id, which is regenerated on every recreate — an install identified by it would refuse to boot after every image upgrade, reading the stamp the previous container had written seconds earlier as another host's. Identity is a persisted id under the state directory.

Rule 63

The complexity bar belongs to the operator, and a password that no longer meets it is replaced on the far side of the second factor. The schema is a shape check; the policy is a setting, failing to the defaults rather than to "no policy". Demanding the change in front of the second factor would be an MFA bypass wearing a policy's clothes.

Rule 64

A passkey is bound to the origin that issued its challenge, the install decides what a passkey is for, and it never names an account that does not already exist. Local accounts only. The relying party is derived from the request and pinned into the ceremony at issue time. Two deployment shapes cannot host passkeys at all and return a reason; two more are misconfigured proxies wearing those refusals.


Rule 65

A delivery test is a specimen of the alert, not a rehearsal against live inventory. The automation wizard's test buttons fire against a made-up device — hostname, IP, MAC, location, model, description, sub-asset and charts are all invented sample data, from the ranges reserved for documentation — and the alert is attached to no asset, so it never appears on a real device's alert list. No reading is quoted; the headline states the condition you configured. The email is marked TEST in its subject, in a banner above the body, and in the plain-text alternative, and that marking is added at send time, so customizing the email template cannot remove it.

Rule 66

A measurement window may be counted in readings, and then the hold counts poll groups. Switch an average, median, minimum or maximum condition from minutes to polls and the window becomes the last N readings that actually produced a value, so a device that drops polls is measured over as many readings as a healthy one. A missed poll is not counted, not filled in and not read as zero: zero would flatter a dying device, and losing polls is what packet loss and down detection are for. (Response time is the one exception — see rule 67.)

Only a counted window takes Sustained for, and there it counts groups: the readings are cut into separate, non-overlapping groups of N, each group becomes one reading, and the alert needs that many groups in a row over the line. So time to alert is group size × sustained groups, which is what the builder's labels promise. A group that has not filled yet is no reading at all. See Triggers → Poll groups.

Rule 67

A missed response-time poll is the timeout it cost, and an outage resets the window. Response time is the one metric whose failure has a duration: a poll that heard nothing waited the device's full probe timeout. So inside a response-time window, a miss that did not put the device Down counts as the timeout configured for that device when it was probed — not skipped, which would let a device answering one poll in ten read as fast as a healthy one.

The fill is only safe because going Down resets the window: everything up to the poll that declared the outage is discarded, so an outage your down automation already paged you for does not keep a recovered device alerting on latency. What counts as Down is your own missed-poll count (rule 36), not a second threshold. A recovered device has no reading until its window refills. Response time defaults to a poll group of 10 in the builder. See Triggers → Response time measures misses too.

Rule 68

Polaris ships two kinds of MIB, and only one of them is yours to remove. The generic IETF/IEEE modules are built in: every install has them, nobody can delete or edit them, and they change only when you update Polaris. That is what makes interfaces, LLDP, PoE, VLAN tables, storage and ENTITY sensors collect the moment SNMP works.

A manufacturer's own MIB is different. Where Polaris ships one — Cisco's today — it is loaded into your MIB Database on the first start of a fresh install, where it sits alongside anything you uploaded and you can delete it. An install that was upgraded rather than installed fresh receives none: your MIB Database is yours, and an upgrade does not add vendor files to it.

A manufacturer profile says which symbol is the CPU and which pair is the memory — the half a MIB cannot tell you. Polaris ships one only where it overrides something the generic MIBs cannot already do, which is why Cisco has one (per-pool memory, and a 5-second CPU where the standard MIB gives a 5-minute average) and MikroTik has none (RouterOS reports CPU, memory and storage through the standard MIB already).

Deleting either is supported and neither is silent. Without the profile, that manufacturer falls back to the standard MIBs — the device keeps being monitored, you lose only the vendor-specific figures. Without the MIB, the profile's rows read unresolved and name the module to re-upload.


Rule 69

A reservation count is of addresses held, and a release is history. Polaris never deletes a reservation when it is given up — it marks it released or expired and keeps it, because that history is how you answer "who had .47 in March". So every count of how full a network is counts only reservations that are active and sitting on an address: the Networks list's Reservations column, its Utilization percentage, and the bar at the bottom of the address list all use that same figure, and they agree by construction. A whole-network reservation holds no individual address and is not counted either — you see it as the network's reserved status instead. The percentage is over the addresses the CIDR can hand out (254 on a /24); where there is no sensible denominator — IPv6 — you get an em dash rather than a 0% bar, which would claim the network is empty.

The unfiltered total still exists and is used for exactly one thing: what a deletion or an archive takes with it. Both remove every reservation row whatever its status, so both confirmations name that larger number before you commit.


Rule 70

Absence from a directory decommissions what that directory manages, and only when the read was whole. Deleting a computer object, or disabling a device account, is how an operator retires a machine — so Active Directory and Entra ID can act on it, the way the vCenter integration acts on a VM that leaves the inventory. The pass is opt-in per integration (Decommission devices that leave the directory, off by default) because the first run of a sweep nobody asked for is a fleet-wide status change.

What decides is the Managed by row on the asset's System tab, not its list of sources. If the directory manages the asset, its word is final — a DHCP sighting, a vCenter record or a reporting agent does not keep a deleted computer object active. If another integration manages it, the sweep only drops its own stale source row.

What refuses it: a scoped Discover Now run; a cancelled or capped read; an empty read (a bind failure or a withdrawn consent, far more often than an emptied directory); and a missing set too large to be ordinary turnover — over 50 devices, or over a fifth of what the integration holds, which is the only guard that catches a narrowed base DN or a half-revoked grant. Every refusal writes a warning naming the reason and changes nothing.

What is not a deletion: a device your OU or name filter excludes, and a device Include disabled skipped. Both are still in the directory, so both keep their asset. A disabled device is decommissioned but keeps its source row.

See Integration-Directory for the operator view.


Reading a rule correctly

Three habits make these easier to apply:

  1. The invariant is the contract; the narrative is the reason. When you need to know whether something is allowed, read the invariant. When you need to know whether to change it, read the narrative first.
  2. Most of these guard against a specific failure — often a silent one. Where a rule looks over-careful, it is usually because the obvious simpler version fails invisibly.
  3. Never paraphrase a rule when quoting it. The wording is load-bearing, and half of them turn on a distinction one adjective carries.

Rule 71

A figure Polaris reports about itself accounts for itself, and a measurement whose mechanism broke says so instead of reading zero. The Maintenance tab's Current size is every relation in the database, and the table list under it accounts for all of it: Polaris's tables as rows, then a line each for the pg-boss job queue, the PostgreSQL catalog, anything in another schema, and an Unattributed residual, then a Total that matches the figure above. Those parts are what the total is made of, not a second measurement of it, so they cannot disagree with it.

Sizes are read from PostgreSQL's catalog rather than by measuring the data directory, which is what keeps the tab instant on a large install — so a figure is accurate as of the last VACUUM/ANALYZE, and the card says so when that matters. Relations that have never been analyzed report zero pages whatever they hold, so the card names how many there are and how many bytes they hide, and warns only when that is material (over 64 MB or 1% of the database) — a few small compressed chunks are normal and get a plain note. The fix is vacuumdb --analyze-only, which is owed after a restore or a PostgreSQL major-version upgrade. "Hypertable sizing is degraded" means Polaris could not read TimescaleDB's chunk catalog, so the sample tables are listed at their parent size — near zero — and their real bytes appear under Unattributed. Neither condition is left to be inferred from a number that looks small: a degraded reading can show a 76.6 GB database whose largest listed table is 1.4 GB, and nothing else on the card would look wrong.

See Server-Settings for the card itself.

Rule 72

A detection script asserts every prerequisite its remediation establishes, and a mode that establishes nothing refuses instead of reporting success. The SSH onboarding scripts Polaris generates come in pairs — a detection half that answers "is this endpoint ready", and a remediation half that makes it ready — and the pairing is what lets a fleet self-heal instead of being configured once and drifting. That only holds if the two halves agree about what "ready" means.

So detection checks the account as well as the key: that it exists, that it is enabled, and that it is a member of local Administrators on Windows (or has its sudoers drop-in on Linux). An endpoint missing any of those cannot have the agent installed on it, so reporting it compliant would be reporting success for something that does not work.

And when you choose use an existing account, the script verifies that account and stops if it is not there or is not an administrator, instead of carrying on to authorize a key for an account that does not exist. It verifies without changing anything — choosing an existing account is not asking Polaris to create one or to promote it.

On Windows, detection also checks the firewall, but only for the state the remediation itself sets up with the same server address (see Rule 76). With an address, it checks for the Polaris rule scoped to that address and for Windows' own rule being off. Without one, it checks that Windows' own rule covers the Domain profile. It never demands anything the remediation would not do, because a check nothing can satisfy would make the pair remediate forever.

One consequence you will see: both scripts refuse to download until you have named the account on the SSH Deployment card, because a detection script without one could not tell you anything useful.

See Polaris-Agent for the card and the scripts.

Rule 73

Planned downtime is reported as planned, and a scoped view of a window still reports the whole window. A device in a maintenance window has its monitor status frozen, so it is deliberately left out of every "down", "warning" and "stale" surface Polaris has (rule 16) — which means a dashboard built from those widgets says nothing at all about the devices that are down on purpose. Active Maintenance is the one surface that does, and two things follow from being the only one.

Its count never wears the red that every other widget's count means "these are down" in. Nobody should act on planned work, so it is not coloured as though somebody should.

And filtering it by region, asset type or FortiGate narrows the list, not the windows in it. Every other widget's rows are devices, so dropping the ones out of scope is right; a maintenance row is a schedule, covering whatever mix of devices it matched. So a schedule appears whenever any of its devices is in scope, and is then shown whole — every device counted, every type named — with the in-scope share stated beside it ("4 devices (2 in this scope)"). The alternative reports a smaller outage than the one actually running, which is the number an operator would size the work and the return time against.

Window times are the Polaris server's wall clock, the same clock the schedule is evaluated against; the countdown beside each one is computed against yours, so the two agree wherever you are sitting.

See Maintenance-Windows and Dashboard.

Rule 74

A field Polaris writes onto a device is budgeted where you type it, and the budget is the device's.

A DHCP reservation's notes are a plain comment on a network Polaris only reads. On a network it pushes to, they are the body of the FortiGate's reserved-address description, which the device holds 255 characters of — and Polaris spends part of that on the wrapper that makes the entry attributable:

Polaris/<user>: <notes> [<hostname>]

The prefix is how a FortiGate admin tells Polaris's entries from hand-made ones. The bracketed hostname at the end is how Polaris reads the hostname back off the gate if it ever has to rebuild from one.

Everything in those 255 characters competes, so the room left for notes is computed, not fixed: a long service-account name or a long hostname leaves less. The form counts it down for you while you type.

Going over is refused — the save fails, naming the budget, what you typed and how many characters to cut. Polaris does not truncate, because a cut costs twice: the comment is shortened on the firewall with nothing said at either end, and the cut takes the trailing [hostname] with it, after which the next discovery would read the tail of the note back as the device's hostname.

Two things this does not do. It does not apply off a pushing network — there is no device field to fit. And it does not block an edit to some other field on a reservation whose stored note is already over budget (one written by discovery, for example): only a save that actually changes the notes or the hostname is judged, so a row can always be shortened rather than being stuck.

See IPAM.

Rule 75

An alert may name many problems on one device, and it ends only when the last of them does.

A PoE fault rarely hits one port. A power supply browns out and eight ports fault inside the same minute — and alerted one per port that is eight alerts, eight emails and eight acknowledge links for one problem.

An automation that watches something per component (per interface, per mount, per sensor) can tick Raise one alert per device, not one per interface on its Actions step. Every affected component is then named on one alert, one acknowledgement covers all of them, and the alert stays up until the last one recovers — the first port to come back does not mail "Resolved" while seven are still down. A component that goes wrong later joins the same alert and sends one more message naming the whole set, and that re-opens the alert if it had been acknowledged: acknowledging "3 ports faulted" is not acknowledging "9 ports faulted". The alert carries the worst severity among the components still affected.

Alert groups (Automations → Alert Groups) go one step further: several automations whose alerts about the same device should read as one. The group decides who is told and how — its notify actions, escalation, reminders, acknowledge-note policy and email layout — while each member automation still decides what counts as a problem. While an automation is in a group it stops delivering on its own. A group can be limited to some devices; on the others its members alert exactly as they would outside it.

A group can also make one alert per automation (every device one automation trips, together) or per device and automation (each automation's findings on a device kept apart). It can also fold by subnet instead of by device: every device it governs whose IP is in the same IPAM network shares one alert, each device a member, and the alert ends when the last of them recovers. A device in no recorded network alerts on its own. Because a subnet group folds devices, it also accepts automations about the whole device (as a per-automation group does) — monitorStatus == down above all — which a device group refuses. See One alert per subnet.

A grouped alert's first email waits one polling interval after the alert opens, so the devices and automations that fail with it are in that first email rather than in a string of updates. The alert shows in Polaris straight away; only the email (and the other actions) waits. An alert that recovers before its first email went sends nothing at all — no "Resolved" either. After the first email, devices joining or recovering are collected and sent as one update email at most once per polling interval.

Turning grouping on or off, changing a group's members, devices or fold, or disabling or deleting a group ends the affected live alerts, and the next check raises them again under whoever owns them now. An alert never changes hands mid-life.

Rule 76

Access is granted on the network profile the endpoint is actually on, and scoping it counts for nothing while a wider rule stands beside it.

Installing the OpenSSH Server capability makes Windows create a firewall rule of its own, OpenSSH-Server-In-TCP. That rule accepts TCP/22 from any source, and it applies to the Private profile only. Both halves matter:

  • A domain-joined endpoint is on the Domain profile, so the rule never applies to it. sshd is installed, running, and unreachable — the service looks healthy and the event log says nothing.
  • On a Private network it opens port 22 to every host on it. Firewall rules are additive allows, so a tightly scoped rule beside it narrows nothing.

The Windows onboarding script settles it, and what "settled" means depends on whether you filled in Polaris server address:

Server address Windows firewall after the run
set Polaris SSH (TCP 22) allows TCP/22 from that address on every profile, and OpenSSH-Server-In-TCP is disabled — the Polaris rule is the only way in
blank nothing is opened, and OpenSSH-Server-In-TCP is widened from Private to Domain, Private so a domain-joined endpoint is reachable. Which sources may connect is unchanged, so restrict port 22 some other way

Public is never added, on either path: being unreachable on your own domain network is the problem being solved, and an any-source rule on the profile a laptop picks up in an airport is not part of it. Both paths are safe to re-run, and the detection script checks for whichever of the two shapes the same server address produces, so a machine left on Private-only is remediated rather than reported healthy.

See Polaris Agent.

Rule 77

A VIP describes an address; it does not claim it — and the status says every fact it has.

A FortiGate virtual IP states what happens to traffic for an address. That is a third fact about the address, beside who holds it and how the gate hands it out (rule 23), and it is never treated as the single answer.

An address behind a VIP can be reserved. The addresses behind a VIP — its mapped addresses, a virtual server's realserver pool — are ordinary hosts that want a DHCP reservation, and holding the external one in the address register is a reasonable thing to want. The VIP rides along: the new reservation carries it, the address keeps reporting it, and the row reads VIP / Reserved. Editing and releasing a VIP row are refused, because that mapping belongs to the device. An interface address is refused outright: it is live on an interface.

The VIP is always visible. One fact per address would hide it — a VIP on a leased address would read "DHCP Lease", and a VIP on a conflicted address "Conflict", even on an address whose reservation was refused because of that VIP. So Status reports the VIP first and what is happening to the address second: VIP / Leased, VIP / Reserved, VIP / Conflict, or VS /… for a load-balance virtual server. Addresses with no VIP show the plain label. The exports of the address list use the same wording, so a PDF cannot disagree with the table it came from.

The per-network Discover button reads the gate's VIP table as part of its pass. A VIP table it could not read is reported as not read — never as "there are no VIPs" — so nothing already recorded is retired on a failed read (rule 53), and a VIP is only ever retired by the gate that owns it.

A VIP deleted on the gate leaves your reservation alone. Integration discovery removes the VIP from a reservation you made, or from a DHCP lease or reservation, once the gate's VIP table no longer lists a VIP of that name at that address. The reservation and its fields stay. This covers networks you built by hand, which the per-network Discover button does not run on, and the same read-or-nothing and owning-gate limits apply.

See IPAM.

Rule 78

An automation may choose to speak for a silenced device, and then it must name who silenced it.

A device behind a down switch or firewall is dependency-down (Dep. Down), and every automation stays silent about it — the outage is the parent's, and one alert on the parent is the whole story (rule 37, rule 16). That is right for the network team and wrong for the people who only watch one device: the operators subscribed to a PLC's down automation hear nothing when the switch above it dies.

So a monitor status is down automation — and only that kind — can tick Dependency-Down Bypass on its Actions step. With it on, the automation still raises its alert the moment the device turns Dep. Down; it does not wait for the device's own missed-poll count, because the upstream's confirmed outage is the evidence. The alert says DEPENDENCY DOWN in the subject, the headline and the message, and names the upstream device — and, when that device is itself Dep. Down under something further up, the device that is actually down (a suppressed switch's FortiGate). If Polaris cannot work out who, the alert still goes out, saying so.

The alert's kind follows the device's state. A plain Down alert on a device that then turns Dep. Down is ended and raised again as dependency-down, naming the switch; a dependency-down alert whose upstream has recovered while the device is still down is ended and raised again as the device's own outage. Neither sends a "resolved" message — nothing recovered.

Three things the toggle does not change. A maintenance window still silences the device. Reminders and escalation still wait while the device is dependency-down — you get one notification, and the follow-ups resume when the upstream is back. And every automation without the toggle stays silent about a dependency-down device.

See Dependency suppression and Automation triggers.

Rule 78a

A dependency-down alert is acknowledged when its root cause's own alert is, and says whose acknowledgement it carries.

When the device that is actually down — the root cause a dependency-down alert names — has its own Down alert acknowledged, every live DEPENDENCY DOWN alert naming that device is acknowledged with it: same person, same note. A dependency-down alert raised after that acknowledgement is raised already acknowledged. It is still delivered; the acknowledgement says the outage is owned, not that nobody else needs to know.

On the Active Alerts widget, hovering the ack pill on one of these alerts shows who acknowledged it, the line Inherited from the root cause's alert on <device>, and the note typed on that alert.

It works one way only: acknowledging a dependency-down alert acknowledges nothing upstream. A root cause that is in a maintenance window or under a dependency test raises no Down alert of its own, so there is nothing to inherit.

Rule 78b

A new automation never makes an acknowledged outage read as unowned.

If a device is down and its Down alert has been acknowledged, a different "monitor status is down" automation that then raises an alert on the same device (one you just created, or one with a longer hold that fires later) raises it already acknowledged, with the same person and note. It is still delivered; its reminders and acknowledgement-stopped escalation stay quiet, because someone already owns the outage.

Hovering the ack pill shows Carried over from another automation's down alert on this device above the note. Only the moment a new alert is raised is covered: acknowledging one down alert does not acknowledge others already open on the device. An acknowledged warning alert does not count, and a grouped alert (rule 75) inherits nothing.

Rule 79

Removing a MAC from an asset is a correction, not a block.

An asset's MAC list is not a list of its network cards. It is every address anything has ever seen that device transmit as — which includes docks and USB adapters (the address follows the dock, not the laptop), randomised Wi-Fi addresses, and identities relayed through ZTNA, plus whatever a merge brought across from another record. So the list sometimes names an address belonging to a different device, and the × beside each entry is how you say so.

What it does not do is blacklist the address. The row is deleted and nothing else; if the network reports that MAC against the asset again, the next discovery run adds it back. That is deliberate, and it is useful:

  • An address inherited from a bad merge, or from a lease on a device that is gone, is never reported again — so deleting it is the whole fix.
  • An address that comes straight back is being transmitted right now. Some physical thing is presenting it alongside this device. Suppressing it would leave you with an asset record that is wrong but looks right.

So a MAC that keeps returning is telling you the association is live, not that the button failed. Go and find the dock.

Removing the entry that is currently the asset's primary MAC Address promotes the best survivor: the device's own cards — as reported by the Polaris Agent, Intune or vCenter — outrank anything a firewall or switch merely saw. A folded port range (AA:…:00 – AA:…:2F) is a block of switch ports rather than a device identity, so it is never promoted; an asset left holding only ranges correctly shows no primary MAC.

Needs Assets: Write (the built-in assetsadmin role, and admin).

See Assets.

Rule 80

Downtime Polaris itself causes is not an incident, and a silence it grants expires on its own.

Upgrading, reinstalling or uninstalling the Polaris Agent stops the agent service on the host. That drops the agent's connection, which raises agent.disconnected — and the built-in automation on that event would page you about work you asked for.

So each of those three operations puts the asset into a maintenance window for its duration. A first install and a retry do not: there is no agent running to disconnect, and silencing a host mid-install would hide a real failure.

Two parts of this are deliberate and worth knowing:

  • It ends when the agent comes back, not when the installer finishes. The disconnect can take up to a minute to be noticed, so ending the window early would let the alert through anyway.
  • It expires on its own — 20 minutes for an upgrade or uninstall, 30 for a reinstall — whatever happened to the operation that opened it. A device in maintenance is not being monitored, so a window that could be left open by a crashed upgrade would quietly stop watching a production machine. If that cap is ever reached, the alert that was suppressed fires late rather than never.

A failed operation ends its window immediately: an agent that is down because its upgrade failed is exactly what you want to hear about.

See Maintenance Windows and Polaris Agent.

Rule 80a

A silence is granted for when the event happened, not for when something got round to reading it.

An agent upgrade's maintenance window is only about two seconds wide — you can see it in the asset's own event list:

10:30:47  agent.upgrade_kickoff     0.19.0 -> 0.20.0
10:30:47  maintenance.entered       Polaris Agent upgrade
10:30:49  agent.disconnected        WARNING
10:30:49  agent.connected
10:30:49  maintenance.exited

The device is in maintenance when the disconnect is recorded. But automations that watch events are evaluated once a minute, against a backlog, so by the time one looks, that window has been shut for most of a minute and the device is back to active. Asking "is this device in maintenance?" at that point would mean any maintenance window shorter than a minute suppresses nothing at all — an agent upgrade, a short scheduled window, or releasing a device from maintenance shortly after something happened to it.

So event automations ask "was it in maintenance when this happened?", checking the device's maintenance history at the moment the event was recorded. In practice:

  • An event that happened inside a maintenance window stays silent, however briefly that window was open and however long ago it closed.
  • An event just outside one still alerts. A device that drops again ten seconds after its upgrade finished is a real outage and you will hear about it.
  • A failed upgrade still alerts. Polaris ends the maintenance window before recording the failure, deliberately, so an agent that is down because its upgrade failed is never covered by the silence its own upgrade was granted.
  • Recovery still clears. The counterpart event that closes an alert is never suppressed — otherwise an alert raised before a window could be left with nothing able to clear it.

One limit worth knowing: this covers maintenance windows. A device silenced by dependency suppression that recovers within the same minute can still produce an alert, because Polaris keeps no history of when suppression started and stopped the way it does for windows.

See Maintenance Windows and Automation Triggers.

Rule 81

Not in use — the number is not reused.

Rule 82

A measurement of the host must not be dominated by the measurer, and a scheduling offset is not a way to protect one.

The Polaris Agent does not measure host CPU over a short sampling window. On a single-vCPU VM such a window is actively wrong: if one of the agent's own collections is still running when the window opens, it holds the only core, and the sample reports close to 100% CPU for a host that is otherwise idle.

The error is not random, which is what makes it worth a rule. The same collections overrun on the same hosts every minute, so those hosts would read high the same way every time, and nothing on the chart would say so.

Spreading the collections across the minute cannot prevent this. An offset controls when a collection starts, not how long it runs, and on the small hosts where this matters everything runs long.

So there is no sampling window. The agent reads the operating system's running CPU counters and reports the difference since its previous sample, which means:

  • The measured span is the whole interval between samples — by default 60 seconds. Nothing goes unmeasured, and the agent's own work can only ever count for what it actually costs.
  • The chart is flatter, and CPU thresholds fire on a sustained average rather than on whichever second happened to be sampled.
  • The sample interval is the smoothing. Shorten telemetry_interval_sec on a host you want a sharper chart for; that shortens the averaging window too.

This applies to host CPU. Per-program CPU still takes a brief sample, because a single program's percentage is measured against elapsed time rather than against the machine — a collection competing with it makes that number read low, not high.

See Polaris Agent.

Rule 83

A serial belongs to one device and one owner; two claimants is a report, never a silent winner.

A serial number is meant to settle arguments, and two situations can make it the argument instead. Polaris reports both on the Conflicts page.

One device, two FortiGates. A FortiSwitch or FortiAP is discovered through the gate that manages it. If two gates both carry it on their managed roster — because the device was moved and nobody removed it from the old gate's configuration, or because two integrations cover overlapping equipment — then whichever integration ran discovery most recently would own the record, and the next run of the other one would take it back. That decides the device's parent for dependency suppression, where it appears on the Device Map, which region tags it carries, and which gate a description sync is addressed to. None of it would be visible: the record would simply say something different depending on which run was last.

The card names both gates and, for each, when it last reported the device. That last column is the one that tells the two explanations apart, because a gate that has genuinely lost the device stops reporting it.

Polaris changes nothing on the devices, and picks no winner. A completed move and a forgotten roster entry look identical for as long as both gates keep answering, and only you know which happened — the fix is on the FortiGates either way. So remove the device from the gate that no longer owns it, and the card closes itself once that gate has stopped reporting it for two days. A stale entry keeps being reported, so it keeps the card.

One serial, two records. The other case is two assets carrying the same serial — usually one device that two integrations both found and nothing cross-linked, or a record that outlived a re-enrolment. Here there is nothing to weigh up: a serial identifies one unit, so the card's action is a merge, either one-click from the row you want to keep or through the full comparison first. Merging needs full read-write on Assets, because it deletes a record. Most of these never reach you: a background pass merges them automatically every 30 minutes, and the card appears only for a group that pass declined or failed to merge.

Serials that identify nothing are ignored rather than reported: the placeholders some hardware ships (To Be Filled By O.E.M., Default string, System Serial Number, Not Specified, in any capitalisation, spacing or punctuation, and a serial that is one character repeated), anything under four characters, and any serial shared by more than three assets — past that count the serial is the problem, not the assets. If two genuinely different units do report one serial, Reject the card and that pair will not come back.

Those placeholders are also refused at the point a serial would be recorded, not just here — see Rule 84.

A retired gate is not a claimant. Once a FortiGate's asset is decommissioned or disabled, its last report no longer counts — so after a swap, the old gate stops contesting the new one's switches and APs straight away rather than two days later.

See Conflict Resolution and Integration: Fortinet.

Rule 84

A serial that identifies nothing is refused when it would be recorded, not when something later reads it.

Hardware is supposed to carry a serial number programmed at the factory. Plenty of it does not, and reports a placeholder instead — To Be Filled By O.E.M., Default string, System Serial Number, a row of zeroes. Every unit of that model reports the same one. Spelling does not matter: DEFAULTSTRING and Default_String are the same placeholder as Default string.

Polaris refuses those values wherever a serial would be recorded, rather than storing them and filtering them later. Three things follow, and they are what you will actually see:

  • An asset shows no serial rather than a fake one. An empty Serial Number field means nothing that saw this device could tell you — not that the value was lost.
  • Polaris falls through to the next source. A device known to both an agent and Intune, where the agent can only read a placeholder, shows Intune's serial. The agent normally outranks Intune for this field; it does not get to win it with a value that identifies nothing.
  • A stored placeholder is cleared once nothing can replace it, and the change is written to Events as asset.serial.cleared. This is why a serial can disappear from a Windows asset after you upgrade its agent — see Polaris Agent. It was never that machine's serial.

Serials are still checked for uniqueness on top of this. A value can be well-formed and still not identify anything — a cloned VM inherits its template's serial, and a machine whose agent has not been upgraded yet keeps whatever it reported before. A serial two different assets both claim is not used to match them.

See Polaris Agent and Conflict Resolution.

Rule 85

A path check measures the path from a host, not the host — and the automation, not the check, decides what failing means.

A path check is run by the Polaris Agent on each matching host, and optionally by the Polaris server itself. Its result describes whether that source can reach the target, so:

  • It never changes the host's status. A laptop that cannot reach the intranet is not a laptop that is down. The host's own Up / Down comes only from its agent's response time.
  • A check has no threshold. You set the SLA in a Path Monitor automation on the path-check metrics (latency, failure rate, HTTP status, pass / fail, hop count, TLS days remaining). It only ever watches hosts with the Polaris Agent installed; the Devices step narrows those, and a tree can't mix path conditions with device conditions. Holds, severity bands, resets, maintenance windows and dependency suppression all work the same as for any other automation. One difference from packet loss: the failure rate has no "ignore readings at or above" ceiling, so a target that fails every run alerts — the host is up and reporting, so no other automation covers it.
  • Response bodies are not kept by default. Every run stores a fingerprint (SHA-256) and the size of the body. Up to 4 KB of the body itself is kept only when a run fails, so you can see what came back, or when you turn on Keep body excerpt for that check.
  • Some targets are refused. Loopback, link-local (including cloud metadata addresses), multicast, IPv6 addresses, URLs with a user name or password in them, and the Polaris server itself cannot be a check target. Private (RFC 1918) addresses are allowed.
  • A route change is an Event, not an alert state. When an agent's traceroute takes a different set of hops from last time, Polaris writes path_check.path_changed to Events (at most once every 10 minutes per host and check). You can alert on it with a Path Monitor automation set to fire when the route changes.
  • The Polaris server is a source, not an asset. Its results are charted and listed under the check's Results. They alert only through a Path Monitor automation with Include the Polaris server's own runs ticked. That works for a single condition, not alongside a custom reset condition, and no device filter, maintenance window or dependency applies to the server. A route change it sees names the check rather than a device. Pointing the server at a target needs Read-Write on Network Discovery as well as on Path Monitor, because the server probes from its own network, where no agent host may be able to reach. Turning the server off, renaming the check or changing its agent hosts does not.
  • A check that signs in runs only from the Polaris server. A web check can authenticate with an HTTP credential (Bearer, Basic or Digest), and such a check is never sent to an agent, so the password or token never leaves the server. Anyone with Read-Only on Credentials can pick a credential and test with it, but not change it. Checks only ever send GET or HEAD.

See Path Checks and Automation Triggers.

Rule 86

An agent that deployed and went quiet has missed its poll, unless Polaris is the one that stopped listening.

Nothing polls a host that is monitored by the Polaris Agent. The agent sends its own response-time readings, so a host that dies, crashes, loses its network or has its agent stopped simply stops sending. Polaris counts that silence as a missed poll.

Once an agent that finished deploying has not been heard from for two polling intervals (and never less than one interval plus a minute), each poll it misses is recorded exactly like a failed ping. The asset turns Warning, then Down when your down-detection automation's missed-poll count is reached, and Asset down fires. At the default 60-second interval with three missed polls, that is about four minutes from the last reading. The chart shows the outage as a dive, and the asset recovers on the agent's next real reading.

What does not count:

  • An agent that has not finished deploying: still installing, failed, uninstalling, or revoked. Polaris does not expect to hear from it.
  • An agent upgrade, reinstall or uninstall. The asset is in a maintenance hold for that (rule 80).
  • Polaris being down. After a restart or an update, every agent gets a full window to reconnect before its silence counts. If no agent anywhere is reporting, Polaris assumes it is the one not receiving (a stopped web service or proxy, say) and records nothing. This check needs at least two agents. With a single agent the two cases look the same, and Polaris alerts rather than staying silent.

The warning-level Agent disconnected automation still fires as well. It describes the agent's connection; Asset down describes the host.

See Polaris Agent and Monitor States.

Rule 87

A firmware image is offered only to a device whose serial names the image's platform, and only forward; the flash takes a hold and never records a version it has not read back.

This covers Fortinet switches, access points and FortiGate firewalls. The Repository files firmware images under a model, but a device is matched on its platform — the token in the image's own header, which is the first six characters of the serial numbers the image was built for. An image whose header cannot be read is stored and never offered. A device is offered an image only when the platform matches and the image is strictly newer than what it runs; a device whose version Polaris cannot read is offered nothing. Never a downgrade — the switch's own compatibility check is consulted too, and a "downgrade" answer aborts before anything is flashed. Polaris does not check Fortinet's supported upgrade path for a FortiGate (which intermediate versions a jump must pass through); the approval dialog says so, and following it is the operator's job.

A model keeps two images, a primary and a backup. Only the primary is offered on its own; the backup is named in the approval dialog when it is also newer than the device. The upgrade request carries the image you approved by name, and it must be one of those two — a click can never push an image nobody looked at.

The same match feeds the automation field firmwareVsPrimary: current, older or newer against the platform's primary image, and no reading at all for a device the Repository cannot place. FortiGates get readings too; the baseline automation Firmware differs from repository primary (informational) is that field with != current, scoped to switches and access points — add Firewall to its scope to be told about gates.

Each upgrade signs in with the login bound in the Repository, the most specific of model, device type and manufacturer. A FortiGate can also sign in with an API token — a REST API credential, or the token of the FortiGate or FortiManager integration that discovered it. A binding that cannot sign in to the device in hand (a token bound where a switch would use it, an integration that holds no token) is skipped, and the next wider binding is used instead.

An upgrade does not start on a device that is down, warning, recovering, behind a parent that is down, decommissioned, quarantined, in storage or disabled; nor while another flash is running on that device, on a switch above or below it, or on its MCLAG peer. A FortiGate in an HA cluster is not upgraded at all: its Firmware card says so, booking is refused, and the upgrade checks the gate's own HA setting again before it sends anything. A device in a scheduled maintenance window is fine — that is when you flash. The flash opens a maintenance window of its own (45 minutes at most) that suppresses everything behind the device. A failed run ends it at once. A run that got as far as the reboot keeps it open until Polaris's own monitoring answers the device again, for up to 10 more minutes: a device's web interface, which the upgrade talks to, often comes back minutes before the SNMP agent that monitoring polls. The window keeps scheduled polling off the device, so during this wait the upgrade runs the device's monitoring probe itself every 15 seconds. It uses the same polling method, so a managed FortiSwitch or FortiAP counts as back when its FortiGate reports it connected, not when it answers a ping. If the probe still has not answered by then, the window ends anyway and the device is judged normally.

The run records what the device reported after it came back; it never rewrites the asset's OS/firmware field itself. The field is updated from what the device itself reports to monitoring, which is the path any other firmware change takes too. A FortiSwitch or FortiAP's version comes from its FortiGate's controller table, read every minute. A FortiGate polled over its REST API reports its version on every poll. Anything else waits for the next discovery. Until the field catches up the card says Flashed rather than offering the same image again. The device's own report outranks the integration's discovered copy for this one field, and it is acted on only when the version really changes. The same version written differently (7.4.5 against v7.4.5) is not a change.

Starting an upgrade needs Read-Write on Assets: whoever may edit an asset may upgrade it. The firmware key covers the Repository only — Read to see it, Read-Write to upload and manage images. Every role with Assets Read-Write can therefore reboot a switch, access point or firewall; the approval dialog and the checks above are what stand between a click and a flash. An upgrade booked for later takes the same approval and the same checks — see rule 93.

See Server Settings → Repository and Assets → Firmware.

Rule 88

Retired — the number is not reused. Leaving unused SD-WAN members out of an alert is governed by rule 98.

Rule 89

A per-core CPU hold follows one core, the alert names it, and it yields to the all-cores alert on the same device.

The CPU core utilization condition (cpuCorePct) finds single-threaded applications. "Above 90% for 3 polls" means the same core was above 90% on three polls in a row. Different cores each spiking once does not count. It raises one alert per device, naming the cores that stayed over the line, with the top five processes by CPU. Only the Polaris Agent, vCenter and Unraid / TrueNAS hosts report per-core figures; any other device has no reading for it.

When the whole device is busy, every core is hot. So while the device has an open CPU utilization alert, the per-core automation does not alert on it, and an alert it already raised clears as superseded. Once the CPU utilization alert clears, the per-core automation can alert again if a core is still hot. Per-core conditions inside a multi-condition automation are not affected.

See Automation Triggers → CPU core utilization.

Rule 90

An SD-WAN member riding a parent that is over the same line does not alert; the parent's alert names the cause.

An overlay (an IPsec tunnel that is an SD-WAN member) rides an underlay port: Overlay-3 over wan2. When wan2 loses packets, every overlay on it loses packets too. Instead of one alert for wan2 and one for each overlay, Polaris raises only the wan2 alert.

This applies to SD-WAN packet loss, latency, jitter and member state. An overlay's alert is held back while its underlay is over the same line in the same automation, or while any automation has an open alert on the same condition about the underlay. It is also held back while the underlay is down: its port is down, or it is dead on any health check. That holds whatever health checks the automation is narrowed to. Polaris learns which port a tunnel rides from the FortiGate's IPsec configuration. An overlay alert that was already open clears as superseded, with no "resolved" notification: the overlay has not recovered, its parent's alert covers it. An overlay whose underlay is healthy alerts as usual.

See Automation Triggers → SD-WAN.

Rule 91

A hostname is not an identity. A name that several switches or access points share is resolved by where the device sits, or not at all.

On a FortiLink fleet the switch name is often set per site, so "IDF-1" exists behind every FortiGate. A device's Last Seen Switch, its Last Seen AP and an access point's uplink switch are all recorded by name. Turning such a name into whichever same-named device turns up first could pick one at another site, and the dependency tree would drift: devices shown under, and silenced behind, a switch they never sat under.

So a shared name is settled by evidence about the device itself: first the switch whose MAC table currently holds the device's MAC, then the FortiGate that last saw the device (or, for an access point, its own controller), then the FortiGate that owns the device's network. When none of those singles out one device, Polaris places nothing rather than guessing. The device then hangs off the FortiGate that saw it, so it is still at the right site. A name that is unique never needs any of this.

91(a) The same holds when discovery compares a new record with the assets already in Polaris. A shared name, including one that matches only on the first 15 characters (the Windows NetBIOS limit), is not offered as a duplicate when the two records disagree about what they are. That is the case when both have serial numbers and those numbers differ, or when one is a switch, router, firewall or access point and the other is a computer, server, printer, virtual-machine host or container. Each record then keeps its own asset. Review cards already waiting in the Conflicts queue for such a pair are rejected by the system on that integration's next discovery run, and an event on the asset records why.

Two switches with different serial numbers are never merged for sharing a hostname. In the dependency tree, rows that share a name show their serial number (or address) beside it, so you can tell them apart before you click.

See Dependency suppression → Rules that keep the graph honest.

A DHCP hostname cannot move a monitored asset. A device chooses the hostname it puts in its DHCP request. When a lease or a FortiGate's detected-device list shows a MAC that no asset has, under the name of an asset that is monitored or has credentials attached, Polaris records the MAC as unverified and changes nothing else: not the address, not the primary MAC, not the type, location or users. Otherwise it would start polling the newcomer with that asset's credentials. Each case writes a discovery.hostname_adopt_refused warning on the asset. An unverified MAC is never used to recognise the device on a later run. Assets that are neither monitored nor credentialed keep matching by name, so a laptop's Wi-Fi and wired addresses still land on one card.

Rule 92

Quiet time withholds the send, never the alert — and what it withheld is reported when it ends. A quiet time belongs to an automation (its Quiet time step) or to the install (Automations → Settings → Global Quiet Times, which applies only to automations with no quiet time of their own). While one is open, an alert it covers is still raised and shown — with a QUIET pill — and its scripts, API calls and audit event still run; what is held is, per severity, whichever of the four people-facing sends the quiet time ticks — the first alert, its reminders, the escalation tiers' first runs, the tiers' own repeats. Holding everything but the first alert is a reminder-only quiet time: the first alert and the all-clear go through, the chasing waits. Only a held first alert owes a summary. Nothing in the alert's clocks moves while a send is held.

When the quiet period ends (or at a chosen send time — refused only when it is inside the quiet period on every day it occurs), one summary email per person goes to everyone the held alerts would have reached — by email even for people who prefer push, in each reader's own time zone — listing the alerts still outstanding and any that recurred more than X times for one device and component, with every time they fired. No graphs; each device opens in Polaris. A held alert that recovers before the summary is not listed, and its all-clear is not sent; once an alert has been named in a summary it behaves like any other. With nothing to list — everything recovered, or nothing was held at all — the summary still goes out as the all-quiet email (on by default), to the people the covered automations notify — device-region routing included, as the users of every region their devices are in — saying nothing is outstanding and, by arriving, that the quiet time and email delivery work. Everyone the quiet time covers is told (92(f)): a summary built from one team's held alerts still reaches every other covered reader with the all-quiet copy. An alert that fired during the quiet period at a severity it does not hold went out live; the summary lists it under Sent during quiet time to the people it reached, who get that list instead of "all quiet". A test delivery is never held. Rule 44 is the hold mechanics inside this.

See Escalation, reminders and quiet time → Quiet time.

Rule 93

A booked flash is approved when it is booked and judged when it fires — on time or not at all, and its recipients hear the outcome either way. A firmware upgrade can be scheduled from a switch, access point or FortiGate's Firmware card by anyone with Read-Write on Assets. You approve the exact image by name when you book, as for an upgrade now (rule 87); if a newer image becomes the model's primary before the booked time, the booked one is no longer offered and the upgrade is not started — it never switches image on its own. The device's health, its login and other upgrades nearby are not checked when you book (a device down now may be fine at the booked hour) but are all checked again when the time comes. If a check fails then, it is not started and not retried — except when an upgrade is still running on the same device, one above or below it, or its MCLAG peer: then it waits and retries every minute for up to two hours past its time. If Polaris was not running at the booked time and only gets to it more than 15 minutes late, it is not started. A device has at most one scheduled upgrade, and every booking names at least one email address. Whatever happens — finished, failed, not started, missed — each address gets one results email, in that reader's own time zone when they have a Polaris account.

See Assets → Firmware.

Rule 94

A workload Polaris restarts or updates is held, one Polaris stops is paused until Polaris starts it, and every attempt is on the record. On an Unraid or TrueNAS SCALE integration, a VM or container (an App on TrueNAS) can be started, stopped, restarted and updated from its asset by anyone with Read-Write on Assets, the same level as a firmware upgrade. Reading its live state needs only Read-Only.

  • Restart and update take a maintenance hold, as an agent upgrade does (rule 80), so downtime you asked for does not page you. The hold is released when the platform reports the action finished, and expires on its own after 10 minutes for a restart and 30 for an update, whatever happened.
  • Stop takes no hold. Instead Polaris pauses monitoring of the workload, unless you tick Keep monitoring while stopped. A Start from Polaris resumes monitoring, but only when it was Polaris that paused it; monitoring you turned off yourself stays off.
  • Polaris never chooses a version. On Unraid, update re-pulls the container's image. On TrueNAS it upgrades the App to the latest version in the catalog, or, for a custom App, pulls its images and redeploys it.
  • Every attempt is an Event named asset.workload.<verb>: the success, the failure, and the refusal (no permission in Polaris, or an API key the platform will not let act).

See Unraid → Workload actions and TrueNAS SCALE → Workload actions.

Rule 95

The assistant answers as the person asking, only reads, and never types a figure into a report. The AI assistant looks things up with your own permissions: if your role cannot read alerts, neither can the assistant when you ask it, and alerts are limited to your regions exactly as on the Alerts page. It can only look things up — it never changes, acknowledges, pushes or deletes anything, and it cannot read stored credentials, API tokens, user accounts, roles, sign-in settings, automation scripts or server settings. A report it builds is a database extract: Polaris runs the query itself (up to 5,000 rows), so the rows you download were never written by the model. Your conversations are visible only to you — administrators included — and are deleted after 90 days without activity. Creating an AI Assistant integration needs Read-Write on Roles and on API Tokens, because it also creates a read-only role and an API token for the model server's own use; the chat itself never uses that token. "Allow loopback" lets the model server be on the Polaris host itself, and allows nothing else that is normally blocked.

Memory (95(i)). The assistant can keep a few short notes about you between conversations. It saves only what you typed in that same message — never something it looked up — and removes a note only when you ask. Addresses, links, passwords, keys and tokens are refused whoever tries to save them. Your notes are visible only to you, capped at 25, and the event log records that they changed, never what they say.

The model server may also be an Azure AI Foundry deployment (95(j)). Every promise above holds the same way on it: lookups still run as you, it is still read-only, and a report's rows still come from the database. It is optional and off unless an administrator adds it. Its API key or Entra ID client secret is stored encrypted, and is never shown again or written to a log.

See AI Assistant.

Rule 96

Polaris never drives a FortiGate's API-key lockout: few requests at once, nothing after a 401 until a pause runs out, and a probe it did not send is not a missed poll. FortiOS 7.6 locks a source IP out of REST API-key access after admin-lockout-threshold failed authorizations (default 3). It counts every non-OK answer, including transient ones, and doubles the lockout on each repeat, so it can grow to hours. Everything Polaris does to a gate comes from one address, so a lockout silences discovery, monitoring, pushes and the Query API tool at once.

  • At most 2 requests are in flight to one gate (per Polaris process; POLARIS_FORTIOS_PER_GATE_CONCURRENCY changes it). A single bad moment costs at most two failed attempts.
  • After a 401 nothing is sent to that gate for 60 seconds, doubling on each consecutive 401 up to 30 minutes. The first answer that is not a 401 ends the pause. Requests during it fail straight away with "Polaris has paused requests to this FortiGate".
  • A paused probe is skipped, not missed. The gate's REST response-time probe, and the controller probe for every switch and AP behind it, record no reading rather than a failure, so a token problem cannot mark a site down.
  • A stored per-stream credential is always shown on the asset. It stays in force on "Inherit", so the Monitoring tab shows it there and Source default clears it. A stale per-asset REST credential hidden behind an "Inherit" stream would otherwise send a burst of bad keys every monitor pass and could keep the server locked out of one gate for hours.

See Monitoring → How Polaris paces FortiOS REST calls.

Rule 97

The all-zero MAC means "no MAC": it never becomes an asset's MAC, and a reservation refuses it. 00:00:00:00:00:00 is not a device's address. FortiGates report it for loopback, tunnel and unconfigured interfaces, and an agent can report it when a host's main adapter is a VPN or virtual NIC. Stored as a MAC, it would let unrelated devices match each other and merge.

  • On an asset, entering it clears the MAC. Creating or editing an asset with an all-zero MAC saves it with no MAC, and a CSV import skips the cell. You are never refused, so an asset that already carries a zero MAC can still be edited.
  • Discovery and the Polaris Agent never record it. An all-zero MAC is left out of an asset's MAC list and is never chosen as its primary MAC.
  • A reservation refuses it. Creating a reservation with an all-zero MAC, or changing a reservation's MAC to zero, is rejected: leave the MAC blank, or use Generate for a device that isn't racked yet. A reservation that already holds a zero MAC (for example, one mirrored from the FortiGate) can still be edited.
  • A zero already stored on an asset is cleared. Each asset whose MAC is cleared gets an asset.mac.cleared event. Reservations are left as they are.
  • Search still finds it, so you can look for any that remain.

Rule 98

An SD-WAN condition may be narrowed to the members whose current address passes a comparison. A member whose address Polaris could not read is kept.

Some deployment templates enable every WAN port on every FortiGate, as SD-WAN members, whether or not a circuit is plugged into them. An unused wan2 is then down on every health check, and a "member is down" automation alerts about it on every gate that has one.

Add SD-WAN member IP address to an SD-WAN member state, latency, jitter or packet loss condition to leave such members out. It compares each member's interface address now, as is or is not an address. Is not 0.0.0.0 leaves out every member with no address.

  • The member is matched to the interface of the same name.
  • 0.0.0.0 matches every form a gate reports for "no address".
  • A member with no interface row, or whose scrape returned no address, is kept. The filter only removes members it has evidence about.
  • A member that stops passing raises no alert, and an alert already open on it clears.
  • The filter is part of what makes two automations "the same trigger" for precedence (rule 18), so a filtered and an unfiltered automation never carve each other out.

It compares the current address only. A DHCP WAN whose lease dropped when its link went down reads 0.0.0.0 too, and is not 0.0.0.0 leaves that outage out.

See Automation Triggers → SD-WAN member IP address.

Rule 99

Polaris never restarts its own container. In a container it asks you to.

On a script install, Restart Polaris to apply on the Capacity Advisor card restarts the Polaris services through systemd. A container has no systemd, so a restart from inside it can only exit. Whether the container then comes back depends on its restart policy, which Polaris cannot see. On Unraid and with a plain docker run the default is "no", and the container stays stopped.

So on a Docker, podman or Unraid install:

  • After you Stage recommendations, the card asks you to restart the Polaris container yourself (docker compose restart for a compose stack, so every role reads the new .env; docker restart <name>; or Restart on your Docker page).
  • The restart endpoint, POST /api/v1/server-settings/restart, refuses with 409.

See Server Settings → Capacity Advisor.

Rule 100

A feed you describe yourself is an inventory list. It never proves a device is on the network or decides what is monitored. It never overrides a source that reads the device directly, and it never reaches beyond its own host.

A Generic API integration can read any REST API. Polaris knows nothing about the system on the other end beyond what you mapped, so it trusts the feed for exactly one thing: this device is in that system's list. In practice:

  • No Last Seen from the feed. A CMDB lists a scrapped device as confidently as a live one. Presence is checked after each run (agent, monitor probe, or ping), as for Active Directory (rule 12).
  • No monitoring decision. A record can be any kind of device, so the integration has no "add as monitored" setting. You monitor its assets as you would a manually added one.
  • Lowest rank among real sources. Where Active Directory, vCenter, Entra, a controller or the Polaris Agent also reports a field, theirs wins. The feed ranks above only a FortiGate's DHCP client name. The sync claims only assets nobody owns, retypes only other, and turns a hostname match into a conflict, never a merge (rule 91).
  • Its own host only. Loopback, link-local and metadata addresses are refused for the API and the OAuth token URL. The request path cannot name another host, next-page links must stay on the same host, and redirects are not followed. Only the token, password and client secret are stored encrypted; extra headers are stored as typed.
  • An incomplete read removes nothing. The opt-in decommission missing setting works as rule 70 does for directories. A run that stopped part way (a failed page, a page or record limit, a repeated page) or an empty read is never treated as deletions.
  • Detail queries add fields, never records. A per-record detail request runs after the list has settled which records exist. One that fails costs that record the fields it would have filled. It never costs the record its asset, and never makes the read incomplete. Values placed into its path are URL-encoded, so a record cannot steer the request to another path or host.
  • It changes only what it wrote. Department, assigned to, purchase order, description, asset tag and the acquired and warranty dates are filled when blank. The feed changes them only while they still hold the value it wrote last time, so your edit, or another integration's, is never overwritten. It never clears one, never writes a description onto a device another integration owns, and leaves off an asset tag another asset already has.
  • A shared configuration carries no secret. The JSON export leaves out the token, password, client secret and any credential-looking header value, and leaves out the host and account unless you ask for them. An import never fills a secret, whatever the file says.
  • It can feed monitoring, never decide it. Monitor queries supply up / down, CPU, interfaces, storage, temperatures, LLDP and custom widgets through the Generic API polling method. A query can make that method the default for its stream on this integration's assets. It never decides which assets are monitored. When the API cannot be reached, a probe is skipped rather than failed, so one outage never marks a fleet down. An asset the API answers for without listing it is down.
  • It can be a network source, below every other. Network queries write subnets, leases, reservations and external IP assignments into IPAM. A network is created only inside a block and never over an overlapping, excluded or retired one. A network another integration discovered is never taken over or written into. A manual reservation is never overwritten: a difference is a conflict. Reservations and assignments the feed drops are released only after a complete read; leases simply expire. Nothing is pushed to a device.

Rule 101

An address is ranked by how directly it is known: a reservation the device holds, then what the gate sees online, then a lease it holds, then ARP. A detected address the gate's own ARP table contradicts decides nothing.

A FortiGate can give Polaris several candidate addresses for one device in a single discovery run: an unexpired lease on a site it left, a reservation and a lease on the same gate, the gate's own detected-device entry, ARP entries, and one of each per network card. Polaris ranks every candidate and uses the winner as the asset's IP address (the one monitoring probes) and its learned location:

  1. How directly the address is known, in this order:
    • a DHCP reservation the device is holding. Someone chose that address for that card, and the device is using it;
    • a detected-device entry the gate reports as online, built from traffic the gate is seeing from that MAC;
    • a DHCP lease the device is holding;
    • an ARP entry: the gate currently resolves the address to the MAC;
    • a detected-device entry for a client that has gone offline, or one that is untrusted (see below);
    • a reservation nobody is holding.
  2. Seen within the last hour. A candidate seen recently beats an older one before anything below is compared. A held reservation the gate last saw more than an hour ago ranks as a lease, so a laptop that comes off its dock moves to its Wi-Fi address instead of staying on the wired address it left.
  3. Wired over wireless. Judged from the FortiSwitch port or FortiAP the gate attributes the device to, the lease's SSID, or the adapter type Intune reports for that MAC.
  4. An address with a reservation. Among a card's ARP addresses, the one a DHCP reservation is configured for wins.
  5. Most recent sighting, then the latest lease expiry.

When a detected-device entry is untrusted. The gate's detected-device entry (and FortiManager's copy of it) reports whichever address it last saw traffic from. It is not trusted to choose the address when the same gate's ARP table:

  • has that MAC on a different address and not on the detected one, or
  • shows that MAC with several addresses, as a card with secondary IPs has.

An untrusted entry can fill in an address for an asset that has none. It never moves an asset off the address it has. When a card's ARP addresses rank equally, the asset stays where it is; pin the address you want (rule 102). A detected address that is simply missing from ARP is not counted against it, because ARP entries expire within minutes.

ARP only overrules an address that this discovery run's DHCP or detected-device data decided. An address that came from vCenter, Azure Arc or an operator is left alone.

A detected-device entry only counts when the gate can show the device is behind it (a FortiSwitch or FortiAP attribution, or an ARP entry). A user connecting through a gate over ZTNA never sets the address. FortiGates, FortiSwitches and FortiAPs keep their management IP and are not ranked this way.

Rule 102

An address belongs to a network card, a card can have several, and you choose which pair the device is monitored on.

An asset's Addresses list shows each MAC with the IP addresses bound to it. A FortiGate adds an address there while it reports the binding as current: a DHCP lease or reservation the device is holding, the gate's detected-device entry for a device that is online, or an ARP entry (which is how a card's secondary addresses appear). When the gate stops reporting a binding, the next successful read of that gate removes it. Addresses typed in by hand and the ones a monitored device reports from its own interface table are never changed by discovery.

Between gate reads, IPAM is the fresher record. Opening an asset's details checks its DHCP lease and reservation addresses against IPAM's subnet for that gate: one IPAM has released, or holds for a different MAC, is removed (an asset.addresses_ipam_reconciled event names it), and a lease IPAM records as a reservation is relabelled. Where IPAM has no subnet covering the address, nothing changes.

The primary pair is the one monitoring probes. Polaris picks it (rule 101) unless you pin one with Set as primary:

  • A pin holds. Discovery does not change a pinned address. If the device stops answering on it, the asset shows down and the address is flagged not seen since.
  • A pin follows a renumbered card. If the pinned address has not been seen for a day and that card now has exactly one recent address, the pin moves to it and an event records the move.
  • One pin at a time. Pinning a pair replaces an address typed in Edit, and typing an address (or clearing it, or Revert to discovered IP) releases the pin. Removing the pinned MAC releases it too.
  • FortiGates, FortiSwitches and FortiAPs are monitored on their management IP and cannot be pinned. Port MACs from a device's interface table are shown collapsed and are never a primary.

Rule 103

Every address carries a confidence score earned from the evidence. Your own choice counts toward it but cannot vouch for itself, and the score only informs.

Each address an asset has is scored 0–100 from the evidence for it. Every piece of evidence counts by how reliable its source is and how recently it was seen, and independent sources that agree add up without ever passing 100:

Evidence Weight Fades by half every
The device's own Polaris Agent reports it on an interface 0.97 day
The device's own interface table (SNMP, REST, WinRM) 0.95 day
Polaris has polled it successfully as the primary up to 0.95 grows with time
A DHCP reservation the device is holding 0.90 12 hours
The FortiManager / FortiGate record for a FortiGate, FortiSwitch or FortiAP names it as the management IP 0.90 day
vCenter's guest IP 0.85 6 hours
The device's own Polaris Agent connects to Polaris from it 0.85 hour
You pinned or typed it 0.80 never
A Proxmox, Unraid or TrueNAS record for the host, VM or container 0.80 6 hours
A FortiGate (or FortiManager) sees the device online at it 0.70 6 hours
A DHCP lease the device is holding 0.70 12 hours
Azure Arc's network profile 0.70 day
A FortiGate's ARP table resolves it to the device's MAC 0.60 4 hours
A DHCP reservation exists for the device's MAC 0.40 never
The FortiGate's endpoint record for the device 0.40 12 hours
A Generic API feed 0.40 day
A FortiGate remembers the device at it, offline 0.30 day
A FortiGate sees the device at it, but ARP shows that card with several addresses 0.20 6 hours

A reservation outweighs what a FortiGate detects because a person chose it: a detected-device entry is the gate's inference from traffic. A FortiGate's ARP table counts for any address it resolves to one of the device's MACs, whichever source reported the address.

The device's own Polaris Agent counts twice. When an agent is deployed, it gets a key that belongs to that one asset, so the addresses it reports on its interfaces come from the device itself; they appear on the Addresses list with the source Agent. The address the agent connects to Polaris from also counts, but only when it is already one of the asset's addresses, is a private (RFC 1918) address, and is among the addresses the agent reports. A loopback, a public address or one the agent's own interfaces don't hold means the connection passed through a proxy or NAT, so it is not counted. It fades quickly once the agent stops checking in.

Polling counts only for the address being monitored, and only while the device is not down: about 0.6 after a day of clean polling, over 0.9 after three. The clock starts at the later of the last failed probe and when the address became the device's, read from the probe history Polaris already holds (up to 30 days back). While polling is answering on an address, none of its evidence fades: the device is demonstrably there, so the age of a FortiGate's last report is not held against it.

Three things count against an address:

  • the device is down on it while a FortiGate sees it online at another address (× 0.3);
  • another device that is present on the network holds it more recently (× 0.5);
  • a FortiGate detected the device at it, but a FortiGate's ARP table has that MAC on a different address and not on this one (× 0.5).

A device that is simply down costs its address nothing, and neither does an address that is merely missing from ARP.

Bands: 80 and over high, 50–79 medium, 20–49 low, under 20 very low.

Before Set as primary or a typed IP address is applied, Polaris scores it on the network's evidence alone, without your choice. Below 50 it asks first, saying what it has seen of the address. A typed address is also pinged once from the Polaris server, and the answer is shown. A reply proves that something answers at the address, not that it is this device.

The score never alerts, never chooses the primary address (rule 101) and never refuses a change. It does one thing on its own: it releases a pin once the network backs it. Every 15 minutes, a pinned address is compared with the asset's other addresses on the evidence the network reports. The comparison leaves out your pin and Polaris's own polling, because only the address being monitored is ever polled. When the pinned address reaches 50 and beats every other address, the pin is released: the address stays, and discovery manages it again. An event records the release with both scores.

A pin is never released while discovery is still trying to change the address. Each time discovery reports a different address for a pinned device, Polaris keeps the pin and notes the attempt. The release waits until three days have passed with no such attempt, so a pin that is the only thing keeping the address where you put it stays in place whatever the scores say.

Unpinning moves the device to the address on its Addresses list with the highest score on the network's evidence. Only when the list is empty does Polaris fall back to what the integrations last reported. Discovery manages the address from then on, so a later discovery run can still move it.

Rule 104

The Active Directory bind account is offered only to a destination discovery chose.

An Active Directory server with no credential of its own is polled over WinRM or SSH with the integration's bind account. That fallback stops applying once someone has typed an address or a hostname on the asset: it then needs a credential selected on it, because choosing a credential is the act that says "send this secret to this address". Until one is selected the asset reports No WinRM credential selected (or SSH). Assets whose address and name came from discovery are unaffected.

Rule 105

A capacity severity change alerts only once it has held, and only the 10-minute capacity check records it.

Polaris grades its own host (disk, database growth, autovacuum, tuning and the other checks on the Maintenance tab's Database card) as Healthy, Watch, Action recommended or Critical. A change of that grade is written as a capacity.severity_changed event, or capacity.severity_recovered when it returns to Healthy. The baseline Capacity severity escalated automation alerts on the first and clears on the second.

  • Only the scheduled check records a change. It runs every 10 minutes with every check the Database card shows. Opening the Maintenance tab or any other page only displays the current grade. It never records one.
  • A change must hold. A grade that differs from the recorded one starts a pending change. Every check that still differs extends it. A single check back at the recorded grade discards it. Once the pending change has lasted for the alert hold (30 minutes by default), the latest grade is recorded and the event is written, saying how long it held. A spike that clears within the hold, such as dead rows waiting for autovacuum, never alerts.
  • The hold applies both ways. A return to Healthy waits it out too, so a host hovering at a threshold cannot clear and re-raise its alert check after check.
  • A gap restarts the hold. If Polaris was stopped or a check failed, a pending change starts again from the next check rather than counting time nobody observed.

Set the hold in Automations: open a capacity automation (the built-in Capacity severity escalated, or any automation whose event trigger matches capacity.severity_changed) and go to its trigger step. Alert after a capacity change holds for runs from Alert on the first reading up to four hours (the API accepts up to a day). It is one setting for the whole install, not a setting of that automation: it decides when the event is written at all, so every capacity automation and the Events log see the same held event, and it saves as soon as you change it. You need write access to automations.

Because the Database card shows the live grade, it also shows what the alerting side last recorded and any change it is currently waiting out.

Rule 106

Every alert email that goes out is recorded on its device, and that record never raises an automation.

When an automation's email is delivered, Polaris writes a notification.email_sent event on the device the alert is about. It names the automation, the To and Cc recipients, and the subject, and it says when the email was an all-clear or was sent because web push could not reach someone. It appears on the device's Events tab beside the alert's fire and clear.

  • Only emails that were actually sent. A send that fails or is still retrying writes nothing.
  • Bcc recipients are counted, never named ("+2 Bcc"), because anyone who can read the device can read its events — the same reason as rule 60.
  • Alerts with no device and test sends write none. A capacity warning has no device tab to put it on, and a test is about an invented device (rule 65).
  • It never fires or clears an automation. An event-triggered automation whose pattern is * or notification.* ignores it; otherwise an automation that emails would trigger itself with every email it sent.

Rule 107

Text you type reaches a command, a script or a generated connection only after it has been checked, and is checked again where it is used.

  • Archive Export accepts a host that is a hostname or IP address, a username of letters, digits, ., _ and - (not starting with a hyphen), and a port from 1 to 65535. Anything else is refused when saved, when tested and before each export.
  • The agent server URL must be a plain http:// or https:// address: a host, an optional port and an optional path of letters, digits and . _ ~ - /. A stored value that fails is ignored with a warning and the derived URL is used.
  • The onboarding scripts accept the Polaris server address only as an IPv4 address or a CIDR range on a single line.
  • The jsign jar is not a setting. Polaris looks for tools/jsign.jar, or for the file named by POLARIS_JSIGN_JAR.
  • A process's log path must be an absolute local path with no network share, no device path and no .., and setting one needs Assets Full Read-Write. Read-Write can still change the log source and clear the path.
  • An asset's address may hold only letters, digits and . _ : % [ ] -, and Open RDP and Open SSH refuse to start from anything else.
  • An API call's body that is JSON has every substituted value escaped for JSON; any other body is sent as written.

Rule 108

A way into the install is checked against the account and the host behind it, not a spelling, a cookie that has outlived its owner, or a code already used.

  • The first-run wizard asks for a setup token before it will touch the host. It is printed where Polaris started (the journal, or docker logs), saved in the state directory as .setup-token, and deleted when setup finishes.
  • Login and API-docs source restrictions hold however the path is spelled, and /metrics answers only its exact lowercase spelling. A page address written with percent-encoding, a doubled slash or a . / .. segment (/%61pi.html, //login.html) is answered 404 before any of these checks.
  • After you sign in, Polaris sends you back only to a page on this server. A remembered destination that would leave the site, or that carries a hidden tab or line break, sends you to the dashboard instead.
  • With LDAP on, a failed sign-in takes the same work whether the name is a local account, a directory account or neither, so response times do not reveal which names exist.
  • Wrong second-factor codes keep counting when the password is typed again.
  • A TOTP code works once. Using it again within its window is refused.
  • Deleting a user, changing their role, or resetting their password, two-factor method or passkeys signs them out everywhere. A session is also re-checked against the account about once a minute.
  • Registering a passkey asks for your current password.
  • Your own password, two-factor and passkey routes need a signed-in session; an API token cannot reach them.
  • Discovery conflicts are decided on your current role, not the one you signed in with.
  • A SAML sign-in response works once.

Rule 109

A value is encoded for the context it lands in, not for the one it came from.

  • A name that contains a quote cannot run script. Integration names, API token names, certificate names, ADOMs and device-reported values are encoded for the place they appear, including inside a button's click handler.
  • Exports are safe to open in a spreadsheet. A cell beginning with =, +, - or @ is written with a leading apostrophe so it reads as text. Plain numbers are left alone.
  • Slack and Teams show device text literally. <, >, & and markdown in a hostname or event message are escaped, so they cannot mention a channel, add a link or change the formatting.
  • Names refuse control characters (line breaks and the like). Quotes and non-English letters are fine.
  • A reported HA peer serial is kept only if it is 6 to 32 letters and digits.

Rule 110

An outbound host is judged by the address it is, not the text it was typed as.

  • Every way of writing an address is judged as the address. 127.1, 2130706433, 0x7f000001 and [::ffff:7f00:1] are loopback and are refused like 127.0.0.1. The refused set is unchanged — loopback, link-local (including the cloud metadata address), unspecified and multicast. Private LAN addresses stay reachable.
  • A name is judged by where it points when Polaris connects. A name that resolves to a refused address is refused, however it got there.
  • Webhooks and API calls do not follow redirects. A redirect answer is a failed delivery.
  • Loopback is allowed only where it is a normal setup. The AI assistant's Allow loopback box and the SMTP channel (a mail relay on the Polaris host) lift the block for loopback and nothing else.
  • Credential Test Connection refuses a typed host that is this machine or a metadata address, and so does a REST API credential's base URL. A typed name that does not resolve is refused, and the address is checked again when the test connects. Testing against an asset is unchanged.
  • Webhooks and API calls are checked on the connection itself, so a name that changes what it resolves to between the check and the send is refused.
  • Web Push endpoints must be https and may not point at this host or a metadata address.

Rule 111

A stored secret follows its destination.

  • Leaving a secret blank keeps it only while the destination is the same. Change an integration's host, port, token URL or connection type, a Proxmox cluster's extra nodes, or an SMTP channel's server, port or security, and Polaris asks for the secret again. A saved secret is never sent to a new address.
  • The same applies to sign-in settings. Changing the LDAP server URL or turning off its certificate check asks for the bind password again; changing the OIDC discovery URL or client ID asks for the client secret again.
  • FortiManager's direct FortiGate connection counts as a destination. Turning off its certificate check, switching from proxy mode to direct, or changing the management interface asks for the FortiGate API token again.
  • Repointing the AI Assistant needs more than Integrations Read-Write. Changing its model server, connection or extra system prompt needs the same rights creating it does (Roles and API Tokens Read-Write), because every user's conversations go there. The change is logged as a warning. Changing only the model does not.
  • Test Connection uses only its own integration's secrets. An integration of another type cannot lend its password to a test.
  • A Fortinet-managed device's address needs Integrations Read-Write to change. Polaris signs in to a FortiGate at the address on its asset record with the integration's API token, so giving a new address to any device a FortiManager or FortiGate integration manages — whatever its asset type — needs more than Assets Read-Write. That covers editing the asset, filling the address from a DNS lookup, importing an invoice PDF, pinning a primary address, giving a device a new address while resolving a duplicate-address conflict, and merging records. Clearing the address or reverting to the discovered one is still an Assets edit.
  • Merging is checked on both sides. A merge that involves a Fortinet-managed device needs Integrations Read-Write unless the merged record keeps a managed device's own address. That stops a hand-made record absorbing a firewall and keeping a typed address; replacing a firewall (an RMA swap) keeps the new device's discovered address and needs nothing more.
  • A switch's or AP's controller is a firewall the same integration manages. An asset someone created by hand with the controller's name is never used.

Rule 112

Aiming a stored credential is allowed, and recorded.

  • Operators are trusted with the credentials they aim. Whoever may edit an asset may give it any stored credential and change its address. Whoever may save or run a Discovery may use any credential against its targets, and whoever may build a path check may give it any HTTP credential.
  • Every such act is written to the event log. One Event per credential names who did it, which credential, who owns it and where it now goes. credential.used (info) means the person created the credential or holds Credentials Full RW. credential.borrowed (warning) means they did not — filter the Events page by it to review credentials used by someone other than their owner.
  • Only a change is recorded. Saving an asset, a Discovery or a path check without changing its credential or where it points records nothing. Running a Discovery records every credential it carries.
  • Routine monitoring is not recorded, only the moment a person chooses where a credential goes.
  • Read-only users walk with the asset's own credentials. An SNMP walk — from the asset's page or the MIB library — by someone without Assets Read-Write may use only a credential the asset already uses. Any other needs Assets Read-Write.

Rule 113

A server proves it is the one Polaris pinned before a password or token is sent to it. Polaris remembers each server the first time it connects (trust on first use) and refuses to send a secret if the server later presents a different identity.

  • SSH — the host key is pinned for every credential unless its Verify the server's host key box is unticked.
  • WinRM over HTTPS and firmware upgrades — when the connection does not verify the certificate against a trusted CA (the usual case with self-signed certificates), Polaris pins the certificate and checks it before sending the password or token. Ticking Verify TLS certificate checks the CA chain instead.
  • Firmware upgrades of Fortinet devices — a factory certificate that names a different serial number than the asset's is refused before signing in.
  • FortiGate and FortiManager — with Verify SSL off, every request carrying the API token (discovery, monitoring, pushes, and a FortiManager's direct connections to each gate in proxy-bypass mode) goes only to the pinned certificate. So do the REST API credential and HTTP checks that send a password or token. A gate whose certificate changes stops being polled until someone looks.
  • After a firmware upgrade Polaris runs itself, the device's new certificate is pinned automatically. Any other certificate change needs a person.
  • Event archive export — the archive server's SSH host key is pinned on first use. If an export fails, events are not pruned that hour, so nothing is deleted without a copy.

A server that presents a different certificate is listed first when you click View pinned certificates under Server Settings → Web Server → Pinned server identities (or on the Windows SSH Deployment card). The slide-over shows each certificate with the asset at that address (click it to open the asset) and the integration that uses it, and a changed one with the pinned and the new SHA-256 side by side; the capacity card names it too. Compare the new one with the server and choose Trust new certificate, which pins exactly that certificate. Each FortiGate's Sources tab shows the certificates pinned for the connections its discovering integration makes — the FortiManager, and in proxy-bypass mode the gate itself. A host that was rebuilt can still have its pin deleted, and the archive server has Forget host key in the event archive settings. Pins belong to an address and port, so on DHCP space an address that moves to a different machine is refused until its pin is deleted. Trust on first use cannot detect a server that was already impersonated when Polaris first connected to it.

Rule 114

A permission reaches only what it says, and a grant never exceeds the granter.

  • No grant above your own level. Creating or editing a role, creating an account on a role, moving an account to a role and binding an API token to a role are refused if they would grant a permission you do not hold yourself.
  • Script actions need Automation Scripts Read-Write in alert groups as well as automations.
  • API call headers are secrets — encrypted at rest, masked when shown, and kept on save only while the call's URL is unchanged.
  • An asset's Alerts tab shows full automation details only to people who can read automations; others see each automation's name, trigger, severity and scope.
  • Adding a contact over a directory-synced entry needs Contacts Full RW, the same as adopting it.
  • Rows a subnet Refresh imports are unowned, as discovery's are.
  • Utilization shows recent reservations only to people who can read reservations.
  • A scoped API token quarantining an asset keeps other sites' quarantine records.
  • Roles flagged for review — custom roles, and the editable built-in roles where they differ from what they ship with, holding Read-Write on a permission that became stronger in the September 2026 update — are listed until an administrator saves them or marks them reviewed. Nothing is taken away automatically.
  • An IdP group mapping is a grant. Mapping a group to a role, changing the group it applies to, or switching it back on needs you to hold everything that role grants. Removing a mapping to a role above your own needs that rank too.
  • Regenerating the AI Assistant's token needs the rights creating it does.
  • Alert groups that run a script need Automation Scripts Read-Write to change their members or scope, not only their actions.
  • The tag filter preview shows how many devices match to anyone who can open it, and which devices only to people who can list assets.
  • Registering an integration's address may overwrite an existing reservation only with Full Read-Write on Reservations, or Read-Write when it is your own.
  • Quarantine is set and released only by Quarantine. Creating or importing an asset with the quarantined status is refused.

Rule 115

A device reboots only on an opt-in held where the authority lives, and a reboot Polaris sends is proved, not assumed.

  • Pressing Reboot needs Read-Write on Assets; being rebootable needs an opt-in. A FortiGate, FortiSwitch or FortiAP is rebootable only through a stored credential with Allow reboots (SSH, REST API token or device admin login), or through its FortiManager / FortiGate integration with Allow reboots. An agent host is rebootable only once it opted in on the agent card — which installs a polkit rule and a marker on the host — and the agent reports the host will do it. A stored credential is used before the integration's token.
  • A managed FortiSwitch or FortiAP is restarted through its FortiGate. The FortiGate is asked to restart it by name, after confirming by serial number that it manages the device.
  • An HA FortiGate is never rebooted, and a FortiGate that answers with another serial number is never sent the reboot.
  • A reboot is reported succeeded only on proof the device restarted — its boot time changed, its FortiGate saw it drop and reconnect, the agent reported a new boot, or monitoring saw its uptime reset. A device that accepted the reboot but was never seen to go down is unverified.
  • Polaris holds the device's alerts while it reboots (rule 80), from the moment the reboot is sent until its monitoring has recorded the reboot — see (a).
  • An agent that does not pick a reboot up within about two and a half minutes never runs it.
  • A scheduled reboot is checked when it is booked and again when it fires, is not sent more than 15 minutes late, and its recipients hear the outcome either way (rule 93).

(a) A reboot Polaris was asked to do does not set off the "Device recently rebooted" automation. That automation fires on the device.reboot Event, which monitoring writes on the first poll that reads the device's uptime after the boot. The Event is always written — it feeds the Recent Reboots widget and the device's history. What keeps it quiet is the maintenance window: the device stays in maintenance until monitoring has recorded the reboot (or, when monitoring reads no uptime for the device and has answered again, at that point), at most 10 minutes after the device is back, so the Event lands inside the window and no alert is raised for it. An agent host's window is not ended by the agent reconnecting, which happens before its first report of the new uptime.

(b) One booked reboot per device, and any other reboot cancels it. A booking exists to get the device rebooted. When it is rebooted first — a Reboot now that succeeds, or anything monitoring sees as an uptime reset (a crash, a power cut, a firmware upgrade, a reboot from its own console) — the pending booking is cancelled with an Event saying why, so the device is not taken down twice. A Reboot now that fails leaves the booking in place, and a booking that is already firing is not touched. No email is sent for the cancellation.

(c) An automation books a reboot; it never sends one. The reboot action (Automation actions) books a reboot of the device that raised the alert — about a minute later, or at the next chosen time of day on the server's clock — through the same booking as the Reboot dialog, so every opt-in and check above applies, again when it fires. It does nothing, and says so in an Event, when a reboot is already booked or the device rebooted within the action's minimum gap (default 24 hours), which stops a device whose memory refills after every boot from being rebooted in a loop. It can sit on the alert's own actions, a severity level's or an escalation tier, never on a reset or all-clear list. A Test button never runs it, export strips it, and saving an automation that has one needs Read-Write on Assets. Its results go to To, Cc and Bcc recipients chosen like a notify action's — including Asset's L1 Region Users — worked out for the alerting device when the automation fires. If To works out to nobody, nothing is booked and the action is recorded as failed.

Rule 116

No pattern can make Polaris hang.

  • Every regular expression you type runs on a linear-time engine (RE2) — log-flag rules, Application Map rules, interface and storage auto-monitor patterns, manufacturer profiles and model parse rules, custom-widget model patterns, asset-type match rules, HTTP checks and path checks. However the pattern is written, matching takes time proportional to the text.
  • Lookahead, lookbehind and backreferences are not supported. Saving a pattern that uses one is refused with a message saying so. A pattern saved before this applied is skipped (it matches nothing) and listed: the Events page shows a regex.pattern_unusable warning for each, and the capacity card counts them until they are rewritten.
  • Wildcards (*, ?) and automation event patterns match as they always have, without a regular expression underneath.
  • Uploaded MIB files are parsed in linear time and only re-parsed when they change.
  • WinRM replies from a Windows host are read in linear time, and a reply larger than 4 MB is refused.

Rule 117

The host keeps Polaris's secrets and privileges to the Polaris account and the HA peer.

  • Updates restart Polaris through one fixed root unit. The updater (Server Settings → Maintenance) starts polaris-update-sync.service. That unit syncs the shipped unit files, the nginx GUI helpers and the HA files, then restarts polaris.target. The polaris account may start that unit and start, stop or restart the Polaris units. It may not run any other unit or command as root.
  • Unit files and the sudoers entry are checked before they are installed. A Polaris unit must run as polaris, may not run anything with full privileges, may hold only one capability (binding a low port), and may depend only on Polaris, network, database and nginx units. The sudoers entry may grant only the nginx helper. A file that fails is not installed, the one already in place stays, and the refusal is in the journal under polaris-updater.
  • Root helpers wait for an administrator. Some files root runs directly: the nginx helper, the update helper itself, the polkit rule, and on an HA node the reconciler, its unit and timer, the SSH command and Patroni's drop-in. An update never installs a new version of these. It holds them, the rest of the update applies, and the Maintenance tab shows Root helper update pending with the command to run: sudo /usr/local/sbin/polaris-update-sync --apply-helpers. That shows each change against the installed copy and installs the ones you confirm. Nothing the polaris account can write is ever run as root without an administrator.
  • Root runs the reviewed copies of the manual scripts. update-linux.sh and the HA setup script are installed as polaris-update-linux and polaris-ha-setup, reviewed like every root helper, and a copy run from /opt/polaris hands over to them. The HA setup script copies its root files only from a source the Polaris account cannot write; otherwise it hands them to the update helper, which shows each one and asks.
  • Re-running an installer, update-linux.sh or migrate-to-nginx.sh asks too. On an existing install they hand the unit files, sudoers and root helpers to the installed update helper, which checks the unit files and shows each changed helper and asks before installing it. Pass --yes to update-linux.sh (or set POLARIS_YES=1 for the installers) to install without asking; without a terminal, changed helpers stay pending.
  • .env is written by the Polaris account, never by root, and is never readable by anyone else, not even while it is being created.
  • The nginx config Polaris installs is checked before it goes live. It may use only the settings Polaris itself writes, with its certificate and key at Polaris's own paths and requests passed only to Polaris on this host. A config that does anything else is refused and the running one stays.
  • Each install gets its own database password, generated by the installer, unless you supply one with POLARIS_DB_PASSWORD. No installer passes a database password on a command line. With an external database, pass the URL in POLARIS_DATABASE_URL, or type it at the hidden prompt. An install whose database account still uses the old shared default password shows a warning on the capacity card, with the steps to rotate it.
  • Backups are readable by the Polaris account only. The backup directory is private (0700) and every backup file is 0600. That covers the temporary file a restore decrypts into and the copy a schedule makes to another path. The pre-upgrade dump from upgrade-node.sh goes to the root-only /var/backups/polaris and is deleted once the upgrade succeeds (keep it with --keep-backup).
  • The HA peers trust each other only after you confirm it. The file sync connects only to a host key you pinned with polaris-ha-role pin-peer. That is a fingerprint you confirmed, or the one the peer reported when it enrolled. Until a key is pinned, the sync refuses and says what to run. The sync key can only read, using the flags the sync itself sends. A new cluster verifies the peer's PostgreSQL certificate on every replication connection. An existing cluster can switch that on with the steps in the HA guide.
  • The Windows agent's configuration (it holds the agent's token) is readable by SYSTEM and Administrators only, from the moment it is written.
  • The Unraid Query API window runs queries only. A document that contains a mutation or a subscription anywhere is refused, and every query run is recorded as an integration.query event. Block strings ("""…""") are refused too; use an ordinary "string" or a variable.
  • An HA node's bundle is collected once, by the node that asked for it. The handle the bootstrap script polls with is stored only as a hash. The download must come from the address that registered, within 10 minutes of your approval, and a registration older than 30 minutes can no longer be approved — generate a new script instead.
  • A new HA cluster's Patroni REST API uses TLS, with each node's cluster certificate, so its write credentials never cross the link between the database nodes in clear text. An existing cluster keeps plain HTTP until you switch it over (see Patroni REST API over TLS in docs/UPGRADING.md); the capacity card shows a warning until then.

Rule 118

A poll that failed is stamped as an attempt, never as an update — and how long a stream has been failing is something an automation can alert on.

  • "Updated X ago" means the last poll that returned data. On the System tab, the CPU & Memory and Interfaces headers show when the stream last answered with data. A poll that failed never moves that time.
  • A failing stream says so. While a stream's polls are failing, its header reads ⚠ last poll failed X ago in amber; hover it for when the failures started, the last successful poll, and the error the device returned. Once the data is older than three polling intervals, the amber banner above the chart or table adds the same error — and a stream that has never succeeded shows the banner with the error straight away.
  • A failing device is retried once per polling interval, the same as a healthy one. A failure does not make Polaris ask again sooner.
  • Two automation conditions measure it: CPU & memory polls failing (minutes) and Interface polls failing (minutes). Each reads how many minutes the stream's polls have been failing, counted from the first failed poll to the latest one; 0 means the stream is healthy. A device whose stream was never polled (no collector for its method, or the stream switched off) has no reading, so a fleet-wide condition never fires on it.
  • They are independent of response time. A device that answers ping or SNMP but whose REST pull is refused reads Up and is still caught. If the device goes Down, the alert is handed to the device-down alert — one outage, one alert — and the reading stops advancing while the heavy streams are paused.

Rule 119

Nothing another account on a monitored host can do redirects what Polaris writes there or reads from it — and the host cannot take Polaris down.

  • Agent installs, upgrades, uninstalls and reboot-permission changes stage their files in a private folder made for that run — a fresh mktemp folder only the SSH account can open on Linux and macOS, and a new folder in the SSH account's own temp folder on Windows (never C:\Windows\Temp). The folder is deleted afterwards. The installer checks the agent binary's SHA-256 before installing it.
  • The agent never follows a link when it reads a log file (agent 0.27.0). A file in a watched log folder that is a symbolic link, a junction or a hard link is skipped, and the agent's own log says which file and why. A link in the part of the path you typed yourself is followed. A log that is rotated is read from the start of the new file, as before.
  • A malformed message from an agent closes that agent's connection only. It cannot stop Polaris.

Rule 120

A quiet time can name the people it does not apply to. A quiet time — global or an automation's own — carries an optional list of recipient exceptions: email addresses that each have their own choice of what is held, under the same hours. Nothing ticked means the address ignores the quiet time. (Escalation, reminders and quiet time has the details.)

  • The send splits by who is held, and by nothing else. The people who get an email at a given moment still get one email together (rule 25). The held part either never goes out (a first alert, which the summary reports instead) or goes out later (a reminder).
  • Held for some, never dropped for anyone. A reminder or escalation tier goes out live to the excepted addresses and its clock moves. The people it was held for get the newest held copy when the quiet period ends, unless the alert has recovered or been acknowledged by then.
  • The summary and the all-clear follow what each person was told. Someone held for nothing gets no summary — no all-quiet email and no list of what reached them live. The all-clear reaches everyone who heard the alert started and skips those who did not.

Rule 121

A region can only be nested under a region that contains it on the map. Nesting is worked out from the polygons: a region inside another sits under it. When several regions contain one (a sub-region inside a state inside a division), you can drag it in the Regions panel onto a bigger one to nest it there instead, which changes the levels — and so who level-routed alerts reach. A drop onto a region that does not contain it is refused. If a later reshape breaks the containment, the region goes back under its smallest container until the shape is restored. On the map, smaller regions always draw on top of the regions around them, so clicking one picks the inner region. (Device Map has the details.)

Rule 122

A group firmware upgrade covers one FortiGate and the devices behind it, and runs from the bottom up. Every selected device must sit under the same FortiGate, and only one group per FortiGate can be scheduled or running. Devices already on the newest image, or with no image in the Repository, are dropped from the schedule. Booking a group cancels a device's own scheduled upgrade, but only once you confirm it. Devices upgrade deepest first (access points, then switches, then the FortiGate), never beside a device above, below or paired with them. A device upgrades only after everything behind it finished cleanly; a failure skips only the devices above it. Health is checked just before each device starts: an unhealthy device is skipped, the next one starts, and the devices above it still upgrade. One summary email lists failures, health skips, devices skipped above a failure, and what was dropped. (Assets has the details.)

⚠️ **GitHub.com Fallback** ⚠️