Help & Documentation

What is Octotor?

IT information lives scattered across independent systems — Active Directory, Microsoft 365, network equipment, and the machines themselves — making it hard to get a quick read or run a deep investigation in one place. Octotor solves both: it gathers information from any script-capable system and presents it through a single, friendly web interface. And it doesn't just collect — it acts: scripts run directly on remote computers, switches, and firewalls, manually, on a schedule, or automatically when an alert fires.

Octotor is agentless: built on industry-standard protocols — WinRM, SSH, and SNMP — it talks to almost any system straight out of the box, with nothing to install on the target. Deployment is fast and simple.

See everything in one place

Every asset — person, computer, switch, firewall, cloud account — has one page holding everything Octotor knows about it: identity, attributes, linked accounts, relationships, history. For the help desk this turns support into a one-glance operation: the moment a call comes in, the caller's full picture is already on screen, and the technician can act immediately — running pre-built toolkit scripts in real time with a single click.

Act on what you see

Select one or several assets, pick a script, and run it instantly — without ever seeing the target's credentials, which stay securely stored in Octotor. The Scheduler runs the same scripts on a clock — a routine AD sync to keep the inventory current, an SNMP query to check switch port status — or triggered by a detected condition instead of time, driving automatic remediation (for example, resetting a VPN tunnel the moment a connection loss is detected).

Know the moment something changes

Alerts fire on a wide range of conditions — an asset going offline or coming back, an attribute crossing a threshold (like low disk space), a system event, a locked account, a failed logon, and more — delivered by email, Telegram, or webhook. An alert can also trigger a remedy: a script that runs on the asset that fired it (or any other asset), so routine problems are handled before anyone files a ticket. See Alert Center.

The right command on the right machine

Before executing actions, Octotor's Identity Verification confirms the target on the other end is genuinely the one you intended — not a stale record, a reassigned IP, or a renamed machine. And permissions run through every layer: which group a user belongs to, which toolkit scripts they may run, which customers they can view or manage, whether their rights are read-only or read-write, and whether sensitive actions require an extra per-action password before anything executes. See Permissions and Security.

Everything on the record

Every action taken through Octotor — and every piece of information it collects — is tracked. Whether a script ran manually, on schedule, or as an alert-triggered remedy; whether a setting changed or a user signed in — nothing goes unrecorded. The Investigate page is the single timeline where you can reconstruct what changed, who or what triggered it, when, and what the outcome was.

AI, your way

Octotor's toolkit is built to expand: with AI assistance, you can write tailor-made PowerShell or shell scripts for exactly what you need and save them to the Toolkit for manual or scheduled use. Operators can also work in plain language through the AI assistant — ask "which servers haven't rebooted in over 30 days?" or say "restart the DNS service on DC01". You choose where the AI runs: a fully local model, entirely offline and air-gapped — no data ever leaves your network — or an external cloud provider for the full power of the latest large language models. Either way, it's your call, and Octotor adapts.

How it fits together

Browser (you) | | HTTPS v Octotor Web UI ──── Octotor API ──── PostgreSQL (data) | |──── WinRM / SSH / SNMP ──── Target assets (PCs, servers, switches, firewalls) | |──── Local AI (offline) or cloud AI (optional)

Page Overview

PagePurposeWho can access
DashboardSystem health overview, recent activityAdmin, Operator, Viewer
AssetsManage devices, people, accounts, attributes, relationshipsAdmin, Operator, Viewer (read-only)
RunExecute scripts and protocols on assets in real timeAdmin, Operator
ToolkitManage the script libraryAdmin
SchedulerRecurring and condition-triggered tasksAdmin
InvestigateUnified events timeline with AI queriesAdmin, Operator (if group allows)
AlertsLive alert center — acknowledge, resolve, investigateAdmin, Operator, Viewer (if group allows)
Alerts AdminAlert rules, notification channels, maintenance windowsAdmin
GooseAI agent chat — run scripts, investigate, manage via natural languageAdmin, Operator (if group includes 'goose')
ServicesBackground service status and healthAdmin
SettingsSystem configuration, users, groups, credentials, tokens, AIAdmin only

Notifications (Toast System)

Octotor uses non-blocking toast notifications for feedback on every user action. Toasts appear briefly in the top-right corner and never block the UI the way classic pop-up dialogs do. All frontend feedback uses this system — there are no native pop-up dialogs in the app.

ColorMeaningDefault duration
Green (success)Action completed — a record was saved, a job started, a password was changed4 seconds
Green — persistentSecurity-critical confirmation (master action password saved / changed / removed; VACUUM or maintenance results with row counts)Until you click it
Red (error)Action failed — form preserved so you can retry4 seconds
Amber (warn)Validation problem — fill required fields, check format4 seconds
Blue (info)Informational update4 seconds

Click a toast to dismiss it immediately. Persistent toasts (security-critical confirmations and VACUUM/prune result counts) will remain on screen until you click — this is intentional so you can read important numbers or confirmations without them flashing away.

Content Security Policy (CSP)

Octotor ships with a hardened CSP that disallows inline scripts and disallows eval() / runtime JS compilation. If you see console messages like "Refused to execute inline script" in the browser devtools, that is expected — the browser is enforcing the policy. This significantly reduces the reflected-XSS-to-RCE attack surface; it does not affect normal use of any page. If a feature appears to misbehave after a Chrome/Firefox update, open devtools > Console and send the full CSP error text when reporting.

What's new. Each release adds or changes features; the list of what is new, what was fixed, and what to do after upgrading is on the public release-notes page: www.octotor.com/release-notes.html.

Dashboard

The Dashboard is your landing page. It provides a quick overview of system health and recent activity at a glance.

What You See

Actions

The Dashboard automatically adapts to your permissions. If you don't have access to the Run page, the Recent Jobs section is hidden. The System Status section is only visible to admins.
Worked example — triage the morning from the status tiles

You open the Dashboard and the Asset Status tiles read Online 142 Offline 3 Warning 1 Unknown 6.

  • Click the Offline 3 tile → you land on Assets pre-filtered to those 3. Open each to see the last successful ping and run a fresh check.
  • Unknown 6 = never-pinged or monitoring-off assets — turn on autoping for any you expect to be up.
  • Recent Events shows a red job failed line — click it to open Investigate at that entry and read the output.
Expected outcome: in a few clicks you've triaged the 3 offline assets and confirmed the rest are healthy, without scanning the full asset table.

Dependencies

None — the Dashboard reads from all other modules but doesn't require any setup to display.

Assets

The Assets page is the central inventory of everything Octotor manages — computers (workstations and servers), switches, firewalls, and users (AD-synced or manual).

Guided tours

The Tour button in the top-right of the Assets page runs a guided walkthrough on the real page. It dims the screen, highlights one control at a time, and explains what it is for. Where a step needs a click, an arrow points at exactly what to click — make the click yourself and the tour follows you, or press Next and it does that click for you.

The Assets page ships with these tours:

Tours that would create or modify something stop one step short of the Save button, so taking a tour never changes your data. That includes the bulk actions: a tour will point at Enable Monitor or Apply and tell you what it would do, but it never presses one.

Every step has a Next button, so you are never stuck. Stop any tour with End tour or the Esc key, and start one again whenever you like from the Tour button. Ending a tour leaves the page exactly as you left it.

The first tour runs automatically on your first visit, once per browser. After that, every tour waits to be chosen from the Tour button.

Worked example — finding a machine's switch port

A user reports slow file transfers on TRI-LT007 and you want to know which switch port it is plugged into, and at what speed.

  1. Open Assets and click Tour (top-right, next to Import CSV).
  2. Choose Which switch port is this machine on? from the menu.
  3. The tour highlights the first row of the Devices table and says "Open the machine you are chasing." Click the machine's name — TRI-LT007.
  4. The record opens and the tour highlights the Attributes tab. Click it.
  5. The tour points at the Network group, which holds connected_switch_ip (for example 10.40.10.2), switch_port (for example GigabitEthernet1/0/14) and switch_port_speed.

What this tells you: if switch_port_speed reads 100 Mbps on a gigabit port, the link has negotiated down — almost always a bad cable or a damaged socket. You have the port number in hand, so the fix can be dispatched without anyone walking to the comms room.

Note: these values come from SNMP polling of the switch itself, so they appear only for machines whose switch is registered as an asset and polled. On an unpolled switch the Network group has no switch fields, and the tour says it has lost its place rather than pointing at nothing.

Creating an Asset

[+ Add] button | v Enter: Name, Type, Hostname/IP, OS Family | v Asset created ──── appears in asset list | v Open asset ──── add Attributes, link Credentials, create Relations

Asset Detail Tabs

TabWhat it shows
OverviewName, type, hostname, IP, OS, status, monitoring toggle, related assets
AttributesCustom fields — email, disk space, RAM, network info, etc. Editable by admins/operators
CredentialsSSH/WinRM credentials linked to this asset. Link or unlink credentials here
RelationsLinks to other assets (belongs-to, has-user, connected-to)
JobsExecution history — scripts run on this asset, with status and output
ChangesFull attribute change history for this asset (every old → new value), with filters by source (manual, collector, import, bulk), user, and time range. Deep-link via assets.html?open=<id>&tab=changes
TimelineUnified chronological view across the asset's lifetime: every offline/online status flip, every alert (fired/resolved), every job run, and every manual action. Filter chips: All / Status / Alerts / Jobs / Manual. Date range: Last 24h / 7 days / 30 days / 90 days / 1 year. Each row is click-to-expand for source/detail/ref. Source data: alert_transition_log (status flips), alerts (lifecycle), action_log (manual), jobs (runs). Read-only.
IdentityIP validity score, DNS resolution chain, verification history, locked IP toggle. See Identity Verification below

Identity Verification

Octotor verifies that each asset's IP address actually belongs to the expected hostname. This prevents mismatched mappings in DHCP environments where IPs rotate between devices.

How It Works — Verification Flow

AUTOPING CYCLE (runs every N seconds for each monitored asset)
│
├─ 1. CHECK LOCKED IP
│   └─ IP locked? ──▶ Skip DNS, score = 5, ping stored IP
│
├─ 2. CHECK CUSTOMER DNS CONFIG
│   └─ No DNS configured? ──▶ Skip DNS, score unchanged, ping stored IP
│
├─ 3. FORWARD DNS LOOKUP (hostname + domain → DNS server)
│   │  Example: "salespc1" + "contoso.com" → query DNS at 10.5.5.10
│   │
│   ├─ DNS server not responding ──▶ Score = 0, reason: "DNS server not available"
│   │                                 ⚠ Admin alert raised
│   ├─ No A record found ──▶ Score = 2, use stored IP
│   └─ Got IP (e.g. 10.5.5.96)
│       └─ Different from stored IP? ──▶ Update stored IP automatically
│
├─ 4. PING (using resolved or stored IP, with fallback chain)
│   ├─ OFFLINE ──▶ Stop here. Score is NOT changed (avoids penalizing offline devices)
│   └─ ONLINE ──▶ Continue to step 5
│
├─ 5. REVERSE DNS LOOKUP (IP → hostname, optional)
│   │  Queries: "What hostname owns 10.5.5.96?"
│   │
│   ├─ No reverse zone / no PTR record ──▶ Score = 3 (Medium)
│   │   Forward lookup OK but unconfirmed by reverse
│   │
│   ├─ Reverse returns MATCHING hostname ──▶ Score = 4 (High)
│   │   Both forward and reverse agree
│   │
│   └─ Reverse returns DIFFERENT hostname ──▶ Score = 1 (Stale!)
│       The IP belongs to a different device — record is stale
│
└─ 6. STORE RESULT
    Update asset: ip_validity_score, ip_validity_reason, ip_validated_at


SSH IDENTITY GATE (runs before every job/script execution)
│
├─ 1. SSH CONNECT to resolved IP
│
├─ 2. RUN HOSTNAME PROBE
│   │  Linux: "hostname"  |  Windows: "[System.Net.Dns]::GetHostName()"
│   │
│   ├─ MATCH ──▶ Score = 5 (Verified — highest possible)
│   │             Proceed with job execution
│   │
│   └─ MISMATCH
│       ├─ Strict mode ──▶ ABORT job, flag asset, self-heal IPs
│       └─ Warn mode ──▶ Log warning, run job anyway, self-heal IPs
│
└─ 3. SELF-HEALING (on mismatch)
    ├─ Search for the actual hostname in customer assets
    ├─ Found? ──▶ Reassign IP to correct asset automatically
    └─ Not found? ──▶ Log "unknown host" for admin review
      

When Is Reverse DNS Used?

Reverse DNS (PTR lookup) runs during the autoping scoring phase (step 5 above) — after a successful forward lookup and only for online assets. It asks: "What hostname does this IP map to?"

Reverse DNS is not required. Without it, DNS verification caps at score 3 (Medium). SSH verification can still reach score 5 regardless of reverse DNS.

To enable reverse DNS, create a reverse lookup zone in your DNS server for each IP subnet (e.g. 5.5.10.in-addr.arpa for 10.5.5.0/24) and enable dynamic updates. DHCP will then register PTR records automatically.

DNS Server Alerts

If a customer's DNS server is unreachable (timeout or connection refused), Octotor:

IP Validity Score (0-5)

ScoreLabelMeaning
5VerifiedSSH hostname probe confirmed identity, or IP is locked
4HighDNS forward + reverse lookup both match
3MediumDNS forward lookup OK, no reverse (PTR) record available
2LowDNS lookup failed, using stored IP
1StaleDNS or SSH mismatch detected — IP may belong to a different device
0UnknownNo DNS configured for customer, no verification done

Attribute Locking

Any attribute on an asset can be locked by an admin. Locked attributes cannot be overwritten by automated sources (collectors, DNS, imports). Only an admin can change a locked value manually.

Locked IP + DNS: When the primary IP is locked, DNS resolution is skipped entirely for that asset. If you want DNS to run and score (for visibility) but not overwrite the IP, leave the IP unlocked — the score will reflect whether DNS agrees.
Worked example — multi-homed assets: A Hyper-V host (or any server) with two NICs legitimately has two A records in DNS — one per interface. Microsoft AD DNS returns those records in round-robin order, so one query may return [192.168.2.3, 192.168.2.14] and the next [192.168.2.14, 192.168.2.3]. Octotor's DNS resolver uses the first record returned, so with the IP unlocked, autoping rewrites asset.ip on every cycle to whichever record DNS handed back first. The asset visibly flaps between online and offline as the alternate address is reached or unreachable, and a status alert fires on every offline transition. Locking the IP is the fix: autoping skips DNS resolution entirely, the operator's chosen IP is preserved, and the ping fallback chain still verifies reachability via asset.ip → secondary_ip → wifi_ip → hostname. For genuinely dual-homed hosts, set the primary as asset.ip, add the second interface as the secondary_ip attribute, and lock — autoping then pings both candidates per cycle and reports online if either responds.

Per-Component Settings

Each identity verification step can be independently enabled or disabled in Settings > Monitoring > Identity Verification:

SettingOptionsWhat it controls
DNS ResolutionOn / OffAutoping DNS lookup and IP scoring
SSH Identity GateOn / OffHostname probe before job execution
Require Successful ProbeOn / OffAbort job if SSH probe fails (timeout, auth error). Only active when SSH gate is on.
Collector Identity CheckOn / OffHostname comparison after script execution
Mismatch BehaviorStrict / WarnWhat happens when any enabled check finds a mismatch
Validity TTLMinutes (0 = never)Score expires after this many minutes and resets to 0 on the next autoping cycle. Default: 60 minutes.

With all checks off, Octotor connects to the stored IP and runs commands without any identity verification. These settings can be overridden per scheduled task — see Scheduler > Identity Override.

NetMap Compatibility

DNS verification works correctly with per-customer NetMap (Tailscale SNAT translation). DNS server IPs are automatically translated through NetMap before queries are sent. The two systems operate at different layers:

The asset always stores the real IP. If an asset was accidentally stored with a translated (NetMap) IP, DNS resolution will correct it to the real IP automatically.

Example: TRIPLEOR-LAB asset on WiFi segment
  Real IP:       10.18.18.50  (what DNS returns, what the asset stores)
  NetMap:         10.18.18.0/24 → 10.18.0.0/24
  Translated IP: 10.18.0.50   (used ONLY for ping/SSH via Tailscale)
  Reverse lookup: queries 10.18.18.50 (real IP) → hostname ✓
      

Verification Mode (Settings > Monitoring)

ModeBehavior on Mismatch
Strict (default)Abort job, flag asset as ip_mismatch, trigger self-healing
WarnLog warning, continue with job execution, still trigger self-healing
OffNo identity check

Customer DNS Setup

To enable DNS-based verification, configure DNS for each customer in Settings > Customers > Edit:

For best results, the customer's DNS should have reverse lookup zones enabled with dynamic updates. This allows Octotor to perform reverse lookups (IP → hostname) for stronger identity verification.

Key Features

Collector updates & pending approval (who wins — collector, import, or you?)

Every attribute value has an owner: the last thing that set it — a collector, a CSV import, or a human edit. Whether a collector's update (or removal) applies silently or asks for your approval follows two simple rules:

SituationWhat happens
Def's Auto-approve is off (Settings > Attributes)Every collector change goes to Pending Attribute Updates — values and removals alike.
Auto-approve on, value is collector-ownedUpdates and removals apply automatically. A removal deletes the value and history records (cleared).
Auto-approve on, value was set manually or by CSV importThe value is protected: a collector removal is never applied silently — it goes to Pending once, even with auto-approve on. This is why you can see pending rows for an auto-approve attribute.
Worked example — the one-time approval handover.
  1. You imported computers from CSV in April; each row set Last Logged-in User. Those values are now import-owned (protected).
  2. Months later the collector runs on TRI-LAB-01 and reports the field is empty on the machine. Because the stored value is protected, a (cleared) proposal lands in Pending Attribute Updates instead of applying.
  3. You approve it. The value is removed, history records old → (cleared), and the attribute becomes collector-owned.
  4. From now on every collector update and removal for that attribute on that asset applies automatically — no more pending rows. If a human edits the value again (or a new import overwrites it), it becomes protected again and the next collector change asks once more.

Second example: a removed M365 license. The license value was collector-owned and the def is auto-approve, so when the tenant removed the license, the next sync cleared it silently — the empty card stays in the drawer and its history clock shows O365_BUSINESS_PREMIUM → (cleared), who and when.

The approval is Octotor asking: "a human put this value here — OK for the collector to take over?" Only a small, fixed set of fields can be cleared by collectors at all (the clear-on-removal contract); everything else ignores empty values exactly as before.

Duplicate Detection

Octotor automatically detects possible duplicate assets during each auto-ping cycle. If two or more assets within the same customer share the same IP address and are both online, they are flagged as possible duplicates.

Resolving a persistent IP mismatch (stale duplicate): sometimes two assets keep colliding on one IP because one of them is stale — e.g. an AD-discovered computer (VM-TEST-01) whose AD/DNS address now belongs to a different live host (OFFICE365). Identity verification correctly flags it, but it recurs every cycle because DNS keeps resolving the stale name to that IP and the AD collector keeps refreshing the stale asset. Resolve it root-cause first:
  1. Fix the stale DNS record (root cause). Remove or correct the A record that maps the old name to the contested IP (e.g. delete VM-TEST-01 → 192.0.2.103). Once it no longer resolves to that address, autoping stops pointing the stale asset at the live host and the mismatch clears on its own — regardless of anything else.
  2. Keep the object in AD but exclude it from Octotor. Delete the stale asset (and its machine account); when the next collector sweep re-queues it, click Reject in the Discovery queue rather than Approve. A rejected entry is suppressed on later sweeps, so the AD object can stay in place without re-creating the asset.
  3. Lock the IP on the correct asset (belt-and-suspenders). Open the genuine owner of the address (here OFFICE365) and lock its IP. Autoping then skips DNS for it and pins the address, so its identity never drifts even if a stale record reappears.
Note: locking the correct asset alone stops its flapping, but the stale asset keeps generating identity_mismatch noise until you address it via step 1 or 2 — and the AD computer collector will keep refreshing the stale asset (no duplicate is created, and it is not deleted — directory deletion-sync is a separate planned feature).

AD User Auto-Discovery

The windows/collect-ad-users.ps1 collector turns a Domain Controller asset into an identity source. Schedule it (or Run it) against a DC; it queries every AD user and emits one record per account (SAM, UPN, email, state Enabled/Disabled/Locked, OU, groups, last logon, password dates, SID, description).

AD Computer Auto-Discovery & Machine Accounts

The windows/collect-ad-computers.ps1 collector does the same for AD computer objects. Run it against a DC; it emits one record per computer (name, DNS hostname, OS, OU, enabled state, last logon, and the ManagedBy owner when set).

Asset Enable / Disable

Assets can be administratively disabled via Asset > Edit > Account Status. A disabled asset:

Disabling an asset is useful for decommissioned equipment, known duplicates, or assets temporarily taken offline for maintenance. Re-enable at any time to resume monitoring and management.

Customer Assignment

Every asset must belong to a customer. When creating or editing an asset, select the customer from the dropdown. The customer is shown in the asset list and detail panel. Assets can be filtered by customer using the toolbar dropdown.

CSV import requires a "customer" column. Rows with unknown customer names are skipped.

SSH Port

Each computer, firewall, and switch asset has an SSH Port attribute (under Attributes tab). Default is 22. If the target listens on a non-standard SSH port, set this attribute and Octotor will use it for both connectivity checks and SSH connections.

Dependencies

Viewers can browse assets but cannot create, edit, delete, or run commands on them.

User Assets & Accounts

Octotor models people and their identities as first-class assets. A user asset (a person) owns one or more account assets — one per identity system the person has a presence in (Active Directory, Microsoft 365, Google Workspace, Slack, monday.com, Jira, local, other). Accounts are linked to their owner via the account of relation, so the person stays a single clean record while every directory keeps its own attribute set, state and history.

The model at a glance

Working with a person

Setting or changing an account's owner

Discovery matches an imported account to a person by display name — so an account whose name doesn't match an existing person (common for firewall/appliance local logins, which carry only a username) lands on a freshly-created duplicate person. Three ways to fix or prevent that, all restricted to the correct kind (a user account can only be owned by a person; a machine/host account only by a computer):

  1. Change owner (after the fact) — open the account's own drawer > Overview. The OWNER row shows the current owner and a Change button. Search a person, pick them, confirm. If the previous owner is left with no accounts, Octotor offers to delete that now-empty duplicate in the same step.
  2. Set owner before approving — on Assets > Discovered Identities, the Owner column has a set-owner button. Pick the real person before clicking ✓ Approve, and the new account links straight to them — no duplicate is ever created.
  3. Link existing — from a person's Overview > + Add account > Link existing, search the account and attach it here (it moves off its old owner).
Worked example — a Check Point local login on the wrong person. The firewall's local user jsmith (email jsmith@tripleor.com) was imported and, because its name didn't match, created a duplicate person “jsmith” — while the real person Jamie Smith already owned the matching AD and Microsoft 365 accounts.
Fix: open the jsmith account > Overview > OWNER row > Change > search “Jamie” > pick Jamie Smith > Change owner. When prompted “the previous owner ‘jsmith’ now has no accounts — delete it?”, choose OK.
Result: the Check Point login now sits under Jamie Smith alongside their AD and M365 accounts, and the duplicate person is gone. On the next firewall sync the link is preserved (an existing account is only refreshed, never re-matched). To stop it recurring, set the owner in the Discovered Identities queue before approving future imports.

Account Providers (Settings)

Settings > Account Providers is the registry of the identity systems Octotor can hold accounts for. Per provider: display name, icon (curated picker), enabled toggle, and a read-only view of its attribute schema with collected-by info. The enabled toggle is the master switch: a disabled provider disappears from the Assets page Type dropdowns (Devices and Users) and from the Add Account modal, but always stays visible here so it can be re-enabled. Fresh installs ship with Active Directory, Microsoft 365, Check Point, Local and Other enabled; Google Workspace, Slack, Monday and Jira start disabled — flip the toggle to use them. Example: to track Google Workspace accounts, enable Google Workspace here, then it appears in the Users Type filter and the Add Account modal. Provider attribute definitions themselves are managed in Settings > Attributes — pick the provider as the asset type. User accounts and machine accounts share one definition list per provider; attributes that don't apply to a kind simply stay "Not set". A provider can only be deleted when no live accounts use it and no collector targets it.

Filling accounts automatically

The AD collectors (windows/collect-ad-users.ps1 + windows/collect-ad-computers.ps1) sweep a Domain Controller and create/refresh accounts through the Discovery queue — see Assets > AD User Auto-Discovery for the full flow, owner matching and the auto-approve setting. Which providers a collector writes to is set per script via the COLLECTS tiles on the Toolkit page.

Two views: Devices & Users (and what “All” means)

On the Assets page the Type filter splits everything into two views: Devices (computers and device-side objects) and Users (people and their identities). Each view opens on a sensible default — Devices on Computers, Users on User (person) — so you first see the clean list of real machines and real people.

Account assets are still excluded from the computer license count. Every provider type — including Check Point — appears in the Type filter, and each account is always reachable from its owner's drawer.

Changing an asset's type

An asset's Type can be changed from its drawer: open the asset, click Edit on the Overview tab, pick the new type and click Save changes. Only the five generic types are available — computer, user, switch, firewall, other. Every applied type change is written to the audit trail (Investigate shows a type_changed entry with the old and new type and the operator).

Worked example: a box was created as computer but is really a managed switch. Open its drawer → Edit → Type: switchSave changes. The asset now shows the switch attribute schema (ports, SNMP fields); its old computer attributes (OS build, CPU, …) are retained invisibly and would return if it were ever re-typed to computer. Investigate shows type_changed: computer → switch.

Two freshness stamps on firewall assets

A firewall's Overview tab shows two separate timestamps, because two different pipelines feed the asset:

Worked example: the account sync ran an hour ago but no collector job has run for 12 days. Last Account Sync shows today; Last Collector shows the 12-day-old date — so a stale CP: License Expiry value is immediately explainable (the collector simply hasn't run), instead of the two freshness signals being blended into one misleading recent stamp. To refresh attributes, run the system-info recipe against the firewall.

Automatic user→computer mapping (primary-user linking)

Octotor links who a computer belongs to — a person→computer assigned to link — from the machine's primary user, automatically. The primary user is the account with the most interactive logons (local, unlock, RDP) on that machine over the last 14 days, read from the Windows Security log by the collector (Linux uses login history). This reflects who actually uses the box, unlike AD's ManagedBy, which usually names whoever joined the machine to the domain (often an IT admin). Octotor no longer uses ManagedBy for ownership — the primary user is authoritative.

Each machine reports three attributes on its next collection: Primary User (DOMAIN\user), Primary User Confidence (share of interactive logons that were the winner, 0–1), and Primary User Source (e.g. eventlog:14d).

What happens automatically

When a collection runs on a computer and its primary user resolves to a known person (via that person's AD account SAM Account Name), Octotor:

This only happens when the detected user accounts for at least a configured share of the machine's logins over the last 14 days (Settings → Collector → Auto-map users to computers, default 50%), or when you set the primary user by hand and lock it (a locked value always wins, at any confidence). Turn the whole behavior off with the Auto-map users to computers checkbox in the same place.

The "Suggest users" panel (fallback for exceptions)

Confidently-mapped machines are handled automatically and don't appear here. On the Assets page, Suggest users opens a panel that lists only the exceptions:

  1. Proposed — a low-confidence match with no current owner. Assign links it; Ignore dismisses it.
  2. Conflict — the detected user differs from the person already linked. Three buttons: Reassign (replace with the detected user), Unlink (remove the owner and stop auto-mapping this machine), Ignore (keep the current owner and stop auto-mapping this machine).
  3. No matching user — the primary user is a local account (or otherwise has no AD account in Octotor), so there's no person to link. Two buttons: Create local user makes a person (named after the username, reused across machines with that username in the same customer) plus a local account (username + host, marked as admin if it's in the machine's local Administrators group) and assigns it to the computer; Ignore dismisses the machine so it stops appearing (e.g. a shared kiosk or a local-admin-only box you don't want to track).

Bulk actions. Nothing is pre-selected — tick the checkbox on any rows (including conflicts) and use Assign Selected, Ignore Selected, or Unlink Selected to act on them all at once, or use the header checkbox to select every row shown.

Reviewing what you ignored. Click Show ignored to list every machine opted out of auto-mapping (via Unlink or Ignore), with its current owner and who opted it out. Restore (per row) or Restore Selected removes the opt-out, so the machine re-enters auto-mapping on its next collection.

Worked example. The collector runs on WS-1042 and finds 10 interactive logons in 14 days, all TRIPLEOR\jsmith → it stores primary_user = TRIPLEOR\jsmith, primary_user_confidence = 1.0. Because 1.0 ≥ 0.5 and jsmith resolves to Jamie Smith, Octotor immediately creates the Jamie Smith → WS-1042 assignment during that collection — no clicks. If WS-1042 had previously been linked to the IT admin (from the old ManagedBy behavior), that wrong link is removed in the same step. You never see WS-1042 in the Suggest-users panel because it's already correct. A machine whose primary user has no AD account in Octotor shows No matching user in the panel and is left alone.
The primary user is only set on machines Octotor can collect from (working WinRM/SSH credential) where someone was interactively logged in during the collection window. Machines nobody signs into (kiosks, always-remote-only servers) simply won't get a mapping. You can also ask the Goose agent: "suggest user assignments for TRIPLEOR" (tool get_user_link_suggestions, read-only).

Pinning an owner — lock the Primary User, not the owner

Auto-map keeps the owner in sync with the machine's detected Primary User. On the next collection it will overwrite a manually-assigned owner to match whoever logged in most — unless you tell it not to. There is no "lock the owner"; there is only "lock the Primary User field", and the owner always follows that field.

Worked example. A shared machine RECEPTION-PC should always belong to Morgan, even though the night cleaner logs in more. Open RECEPTION-PC → Attributes → set Primary User = TRIPLEOR\mreed → click the lock. From now on every collection leaves it as Morgan; the cleaner's logins no longer flip the owner. Remove the lock later and auto-map resumes.

Local administrators on a computer

The collector script windows/win-user/usr-local-admins.ps1 (a JSON collector) records the membership of each computer's local Administrators group into the Local Administrators attribute (under the computer's Attributes, Software & Users group). Each entry shows the Name (DOMAIN\principal), Type (User/Group) and Source (Active Directory or Local).

Worked example. On HELPDESK-01 the attribute lists TRIPLEOR\jsmith (User, Active Directory), HELPDESK-01\Administrator (User, Local), and so on. To find every computer where a jsmith principal has local-admin rights, go to Assets → attribute filter → local_admins contains jsmith. It's a substring match across the whole list, so it finds both the AD and any local JSMITH entry.

Linux computers report the same Local Administrators attribute — the members of the sudo and wheel groups — collected by linux/collect-info.sh (folded into the standard collection) or the standalone linux/local-admins.sh. The table, drawer render and local_admins filter are identical to Windows, so local_admins contains jsmith matches Linux and Windows machines together.

DHCP client & server (computers)

Every computer collection records whether the machine's primary network adapter gets its IP from DHCP and, when it does, which DHCP Server issued the lease. Both appear under the computer's Attributes → Network group: DHCP Client (Yes/No) and DHCP Server (the server IP). On Windows they come from the adapter's IP configuration; on Linux from NetworkManager / the DHCP lease file.

Worked example. A workstation on the office LAN shows DHCP Client = Yes, DHCP Server = 10.20.0.1. A server with a fixed address shows DHCP Client = No (and no DHCP Server). To list every statically-addressed machine, use Assets → attribute filter → dhcp_client equals false.

Password Never Expires (AD accounts)

The AD user collector (windows/collect-ad-users.ps1) records the Password Never Expires flag on each Active Directory account (under the account's Attributes → Security section, Yes/No). It's a password-hygiene signal — accounts whose passwords never expire are worth reviewing.

Worked example. To audit which AD accounts have non-expiring passwords, open Assets → filter Users → attribute filter → password_never_expires equals true. A service account like svc-backup will typically show Password Never Expires = Yes.

Run

The Run page is where you execute scripts on target assets and follow multi-step protocols. It has three tabs: Command, Protocol, and History.

Running a Script

1. Select target asset(s) | v 2. Select a script from the folder list | (filtered by your group permissions) v 3. Select credentials (auto-selected if defaults exist) | v 4. Click [Run] ──── confirmation prompt | v 5. Connectivity check: | Ping target ──── reachable? ──── proceed | | | Ping failed ──── check SSH port (TCP) | | | SSH port open? ──── proceed | | | Both failed ──── prompt: | [Proceed Anyway] or [Abort] v 6. Octotor connects via SSH ──── runs the script | (uses SSH Port attribute if set, default 22) v 7. Live output streams to your browser in real-time | (output is capped at the Max Job Output size — see below) v 8. Result: done / error ──── saved to job history

Multi-Asset Execution

Select multiple assets using the checkboxes. Octotor runs the script on each asset in parallel. Each asset gets its own output tab showing individual results.

Max Job Output

Per-job stdout/stderr is capped to prevent runaway scripts (e.g. accidentally tailing a multi-GB log file) from saturating the worker container. The cap is configured in Settings > Instance > Max Job Output (MB), defaults to 10 MB, range 1–1024 MB.

When a job's output exceeds the cap, the worker appends a one-line truncation marker, broadcasts it to the live view, and stops appending further output. The script continues to run to completion — only the captured-output stream is bounded. Exit code, action-log entries, and any side effects on the target (file writes, state changes) are preserved.

For very large outputs, capture to a file on the target asset (e.g. ... > C:\Temp\out.txt) and retrieve via SCP/SMB rather than streaming through Octotor.

Running a Protocol

1. Switch to Protocol tab | v 2. Select a protocol ──── select target asset | v 3. Protocol steps appear as a checklist | v 4. Each step is either: | AUTO ──── runs automatically | HUMAN ──── waits for you to click [Confirm] v 5. Step output displayed inline ──── proceed or abort

Credential Selection

PrioritySourceDescription
1Manual selectionYou pick a specific credential from the dropdown
2Asset-linkedCredential directly linked to the target asset
3Per-customer defaultCredential set as default for this customer + OS family
4Global OS defaultCredential set as default for the asset's OS family (linux/windows/mac)
5FailNo eligible credential found — error shown

History Tab

Browse past executions. Click any entry to view the full output, operator who ran it, timing, and status.

Admin users can also enter raw commands directly. This option is hidden for operators — they can only execute pre-defined scripts from the Toolkit.
Worked example — check disk space on a Windows server
  1. Run page → Command tab → pick target WIN-DB01 (a Windows asset).
  2. Script list → Windows folder → disk-free.ps1.
  3. Credentials auto-select the ACME_WIN_ADMIN windows-OS default — leave it.
  4. Click Run → confirm. Ping succeeds, WinRM port 5985 is open, the script executes.
  5. Live output streams: C: 42.1 GB free of 120 GB (35%); exit code 0; status done.
Expected outcome: the run lands in the History tab with operator, timing and full output. If C: had been low, you'd pair this with a disk_free threshold rule (see Alert Center) so Octotor tells you automatically next time instead of you checking by hand.

Dependencies

Toolkit

The Toolkit is the script library. It contains system scripts (built-in, read-only) and custom scripts (created by admins). Scripts are organized by folders: Linux, Windows, Mac, Network, and Custom subfolders.

Script Types

TypeBadgeWho can editLocation
System scriptsSYSTEMNo one (read-only, baked into the image)linux/, windows/, mac/, network/
Custom scriptsUSERAdmin onlycustom/, custom/group-name/

Supported Script Extensions

ExtensionLanguageTypical OS
.shBash / ShellLinux, Mac
.ps1PowerShellWindows
.pyPythonAny
.batBatchWindows
.cmdCommandWindows
If you omit the extension when creating a script, Octotor auto-appends one based on the selected target OS (.sh for Linux/Mac, .ps1 for Windows).

Creating a Custom Script

1. Click [+ New Script] | v 2. Enter: name, folder, target OS | v 3. Paste script content | v 4. Click [Validate] ──── AI reviews the script | | | Verdict: OK / Warning / Blocked | | v v 5. Click [Save] ──── script added to toolkit (blocked if AI verdict is "blocked" unless override is enabled in Settings)

Script Properties

Writing a Collector Script

A collector script is just an ordinary toolkit script with the Collector flag turned on. After the job finishes, the runtime parses the script's stdout, matches every key in that output against Settings > Attributes (the asset_attribute_defs table), and writes the matching values onto the asset.

The contract

Worked example — removed M365 license clears automatically

  1. User tbrooks@tripleor.com shows License = O365_BUSINESS_PREMIUM on their m365 account asset (collected by the M365 sync).
  2. The tenant admin removes Taylor's license in the Microsoft 365 admin center.
  3. On the next M365 sync, the collector reports "license": "" for Taylor (evaluated, gone). Since the license def ships with Auto-approve on, the attribute is cleared immediately — the drawer no longer shows a license, and the attribute history records O365_BUSINESS_PREMIUM → (cleared) with source collector.
  4. If an operator had manually typed that license value earlier (source manual), the sync would instead add a (cleared) row to Assets > Pending Attribute Updates — approving it deletes the attribute, rejecting keeps the manual value.

Discovering the keys you can emit

Three ways to get the canonical key list — pick whichever fits your workflow:

  1. Browse — Settings > Attributes shows every key + label + type + auto-approve flag.
  2. APIGET /api/attributes/defs?asset_type=computer returns JSON for the current asset type.
  3. ExportGET /api/attributes/defs/export downloads seed-attributes.json with every def the system knows about.

Example — query RAM on a Linux/macOS asset (Bash)

Targets attribute keys ram_gb and ram_free_gb (both data_type=number; ram_free_gb is auto-approve).

linux/check-ram.sh
#!/bin/bash
# check-ram.sh — emit RAM totals as JSON for the Octotor collector.
# Mark this script as Collector with parse_mode=json in Toolkit.

RAM_MB=$(free -m | awk 'NR==2{print $2}')
RAM_FREE_MB=$(free -m | awk 'NR==2{print $7}')
RAM_GB=$(awk "BEGIN{printf \"%.1f\", $RAM_MB/1024}")
RAM_FREE_GB=$(awk "BEGIN{printf \"%.1f\", $RAM_FREE_MB/1024}")

cat <<EOF
{
  "ram_gb": $RAM_GB,
  "ram_free_gb": $RAM_FREE_GB
}
EOF

Example — query RAM on a Windows asset (PowerShell)

Same two attribute keys, formatted as a numeric JSON object via ConvertTo-Json.

windows/check-ram.ps1
# check-ram.ps1 — emit RAM totals as JSON for the Octotor collector.
# Mark this script as Collector with parse_mode=json in Toolkit.

$os = Get-CimInstance Win32_OperatingSystem
$ramTotalGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 1)
$ramFreeGB  = [math]::Round($os.FreePhysicalMemory     / 1MB, 1)

$payload = [ordered]@{
  ram_gb      = $ramTotalGB
  ram_free_gb = $ramFreeGB
}

$payload | ConvertTo-Json -Compress

Add your own custom attribute — end to end (antivirus example)

Say you want every computer to show whether an antivirus product is installed, and you want to filter on it. Three steps, no code changes to Octotor:

  1. Define the attribute. Go to Settings > AttributesAdd attribute. Asset type computer, Key antivirus_installed, Label Antivirus Installed, Type boolean, Auto-approve on (so it writes without a pending review). The key is the contract — your script must emit exactly this name.
  2. Write a script that outputs that key. Anything on stdout that isn't the JSON object is ignored, so emit one JSON object. The value type must match the def (boolean here). See the copy-paste example below.
  3. Flag it as a Collector. Upload/enter the script on the Toolkit page, open it, turn on Collector, set Parse Mode JSON and OS Family windows. Run it (or schedule it on the Scheduler) against your computers.

On the next run the collector writes antivirus_installed onto each computer. It then shows in the computer's Attributes, and you can filter it on the Assets page (attribute filter → antivirus_installed equals false to find unprotected machines) and ask the Goose agent about it.

windows/check-antivirus.ps1
# check-antivirus.ps1 — report whether a real antivirus product is registered.
# Mark this script as Collector with parse_mode=json in Toolkit.
# Requires the attribute def key `antivirus_installed` (boolean) to exist first.

$ErrorActionPreference = 'SilentlyContinue'

# Windows Security Center lists registered AV products (excludes Defender-only-off cases).
$av = Get-CimInstance -Namespace 'root/SecurityCenter2' -ClassName 'AntiVirusProduct'

$installed = [bool]($av | Where-Object { $_.displayName })
$names     = ($av | ForEach-Object { $_.displayName } | Where-Object { $_ }) -join ', '

# Emit the exact attribute keys. `antivirus_installed` must be a registered boolean
# attribute; `antivirus` (text) is optional — add that def too if you want the names.
$payload = [ordered]@{
  antivirus_installed = $installed
  antivirus           = $names
}

$payload | ConvertTo-Json -Compress
The one rule that trips people up: the JSON key must exactly match the attribute def key (case-sensitive). Emit antivirus_installed but register av_installed and the runtime logs skipped: antivirus_installed(unknown_key) and writes nothing. Define the attribute first, then match the key.

How to know it worked (no shell access needed)

  1. Save the script in Toolkit and toggle Collector ON (action password may be required).
  2. Run it once on a test asset via the Run page.
  3. Open that asset's drawer (Assets > click the row) and switch to the Jobs tab. Your run appears at the top with a collected N badge — that N is exactly the number of attributes the runtime auto-applied from the script's output. collected 0 means the script ran successfully but the runtime didn't accept any of the keys it emitted.
  4. Expand the job row in the Jobs tab to see the script's full stdout — that's the JSON your script actually emitted. Compare it to the registered keys in Settings > Attributes: any key not on that list is treated as unknown_key and silently skipped, and any value that doesn't match the def's data_type is treated as type_validation and skipped.
  5. Switch to the Attributes tab — if a key landed and was auto-approved, the new value appears under its group (HARDWARE / NETWORK / etc.) with an AUTO badge and a "from collector script" subline.
  6. If the def has auto_approve=false, the value goes to the pending queue instead — open Assets > Pending Attribute Updates and you'll see a row with old → new values waiting for an admin to approve or reject.
  7. For a full audit trail of what changed and when, use the asset's Changes tab and filter by source = collector. Each row shows old → new, the timestamp, and which collector:job#N wrote it.
  8. If your collector is wired into a scheduled task that drives an alert rule, the Alert Center detail panel Source row will read "Scheduled task · <your task name>" + "collector script <your script> updated <key> (job #N)". End-to-end chain complete.
Per-key skip reasons (unknown_key / type_validation / locked / value_too_long / empty_value) are recorded on the job row in the database but not yet surfaced in the UI — for now, the fastest way to diagnose collected 0 is to compare your script's stdout (visible in the expanded Jobs row) against Settings > Attributes by hand. A "collector dry-run + skip-reason inspector" UI is planned for a future release.

Skip-reason vocabulary

ReasonMeaningFix
unknown_keyEmitted key has no matching def for this asset_typeCheck Settings > Attributes for the exact key
type_validationValue didn't match the def's data_typeNumber = numeric literal, boolean = true/false, date = ISO-parseable
value_too_longValue exceeds 1000 charactersTrim or split into multiple keys
empty_valueValue is "", null, or undefinedOmit the key entirely instead of emitting a blank
lockedAdmin locked this attribute on this assetUnlock from the asset's Attributes tab if intentional
If your script emits free-form text instead of structured JSON, set Parse Mode to ai. The runtime sends the raw output plus the attribute-def schema to the local LLM (Ollama via LiteLLM), which extracts a matching JSON object. Trade-off: every collector run pays for an LLM call, and the result is only as good as the model — for fixed-format scripts, JSON parse mode is faster and deterministic.

AI Validation

Octotor uses a local AI model to review scripts before saving. It checks for:

AI validation can be skipped via Settings > "Skip AI Validation for Scripts". The AI block override setting allows saving blocked scripts as warnings.

Risk Classification

Every script is assigned a risk level based on AI validation. The risk level determines whether an action password is required to execute the script.

LevelBadgeMeaningExecution gate
HighHIGHDestructive or privileged operationsRequires action password
MediumMEDSystem modificationsRequires action password (if configured)
LowLOWRead-only or informationalNo password required

Risk levels are assigned automatically by AI during validation and displayed as colored badges in the script list and detail panel. Admins can change the risk level manually; downgrading requires the risk_downgrade action password (see Security).

Content Hash

Each script has a SHA-256 content hash (visible in the detail panel) that is recalculated on every save. This allows admins to verify a script hasn't been modified outside the UI.

Dependencies

Uptime collection

Computer assets carry an uptime_seconds attribute (integer; seconds since last boot). Two paths populate it:

The Identity & Status row in the asset Overview tab renders the value adaptively: 15 seconds / 1 minute 15 seconds / 2 hours / 5 days 4 hours 22 minutes 15 seconds. Trailing zero components are trimmed; middle zeros are preserved (so a host up exactly 1 day and 15 seconds renders as 1 day 0 hours 0 minutes 15 seconds, not the misleading 1 day 15 seconds).

Threshold-rule conversion table

Alert rules compare against uptime_seconds directly. Common thresholds in seconds:

Wall clockThreshold valueExample rule
1 hour3600uptime_seconds < 3600 — fires when an asset rebooted in the last hour
1 day86400
7 days604800
30 days2592000uptime_seconds > 2592000 — warn that the asset is overdue for a reboot
90 days7776000uptime_seconds > 7776000 — critical: patching cycle is missing
1 year31536000

No default uptime rules are seeded — create your own under Alert Center > Rules > New Rule, pick the uptime_seconds attribute, choose a comparator and value from the table above.

Worked example rules

Two concrete examples for common uptime alert needs. Open Alert Center > Rules > New Rule and use the field values verbatim.

Rule 1 — Long uptime (reboot overdue)

Notifies on assets up for more than 3 months (90 days).

FieldValue
NameLong uptime — reboot overdue (>90d)
Severitywarning (orange — reboot reminder, not a crisis)
Categoryavailability
Rule typethreshold
Threshold attributeUptime (seconds since last boot)
Operator>
Value7776000 (90 days × 86 400 s/day)
Dedupe window (min)1440 (24 h — fires once per day per asset while above threshold)
Cooldown (min)10080 (7 days — minimum gap between re-firings, weekly nag not daily)
Notify channelsWhatever you've configured (email / Telegram / Slack / webhook)
Target scopeLeave empty — uptime_seconds only exists on computer-type assets per the def

After save, every asset that's been up > 90 days fires once. After a reboot, the rule auto-resolves on the next collector cycle (uptime crosses back below 7,776,000). If 30 days later it's back above 90 d, the rule fires again.

Rule 2 — Recently rebooted

Notifies on assets up less than 3 hours.

FieldValue
NameAsset recently rebooted (<3h)
Severityinfo (or warning if you want it more prominent)
Categoryavailability
Rule typethreshold
Threshold attributeUptime (seconds since last boot)
Operator<
Value10800 (3 h × 3 600 s/h)
Dedupe window (min)60 (1 h — surfaces each reboot, suppresses redundant fires within the rebooted window)
Cooldown (min)240 (4 h — slightly larger than the 3-h rule window so a re-fire only happens after the asset crosses 3 h and then reboots again)
Notify channelsSame as Rule 1
Target scopeLeave empty
Rule 2 cadence gotcha — schedule uptime.{ps1,sh} hourly

The uptime value on each asset only updates when a collector script runs. The full collect-info cadence is typically every 4 hours, so a 3-hour window can be missed entirely between two collector runs:

09:00  Asset reboots             (uptime = 0)
10:00  Asset is at 1 h uptime    (no collector running)
11:00  Asset is at 2 h uptime    (no collector running)
12:00  Asset is at 3 h uptime    (no collector running — exactly at the threshold)
13:00  collect-info runs         (uptime = 4 h is what gets recorded)
13:00  Rule 2 checks "is uptime < 3 h?"  →  NO (it's 4 h)  →  rule doesn't fire

Result: asset rebooted, but you never get the "recently rebooted" alert. The 3-hour window came and went between two collector runs.

Fix: schedule the dedicated uptime.ps1 / uptime.sh scripts on a cadence shorter than the rule window. Configure at Scheduler > New Task.

Cadence trade-offs:

CronChances per 3-h windowVerdict
*/15 * * * *12Overkill
*/30 * * * *6Solid but more than needed
0 * * * *3Recommended
0 */2 * * *2 (worst case 1)Tight — risky if one job misses
0 */4 * * *1 maxEquivalent to full collect-info — dedicated script adds nothing

Rule 1 doesn't have this concern — the default 4-hourly collect-info is plenty for a "uptime > 90 days" check.

Storm-cap awareness

If a maintenance window reboots 100 hosts at once, Rule 2 would fire 100 times within a short window. Octotor's per-rule storm cap (default 100/min, configurable per-rule) prevents the notification firehose — beyond the cap, alerts are recorded with storm_suppressed=true and the notification channel just gets a single "+97 more" summary instead of a flood. Consider setting storm_cap to e.g. 20 on Rule 2 if you're worried about mass-reboot churn.

Scheduler

The Scheduler runs scripts automatically on a recurring schedule. Tasks are defined using cron expressions and can target assets by type, attribute values, and customer scope.

Besides Script tasks, two non-asset task types exist: Goose Recipe (natural-language instruction executed by the AI agent) and M365 Sync (triggers a Microsoft 365 connector sync — see Help > M365 Connectors, Part 6). Non-asset types hide the target/execution sections; an M365 Sync task needs only a connector and a schedule.

Trigger: Time schedule vs On alert (auto-remediation)

Every task has a Trigger: Time schedule (the classic cron behavior) or On alert. An On-alert task carries no cron — it runs only when an Alert Center rule that links to it fires. This turns any monitoring Octotor does (offline/status, threshold, custom checks, events) into an automated response. Pro tier; attaching an action to a rule is admin-only.

To wire one up:

  1. Scheduler > New task → set Trigger = On alert. Pick the script, target, and credential as usual. Under Alert-trigger options choose Run on:
    • The task's own target — runs against the assets the task's target filter selects (e.g. a specific firewall).
    • The asset that fired the alert — runs against whichever asset tripped the rule. Because Octotor fires per asset, a rule matching 5 of 7 hosts runs the task on those 5 hosts individually.
  2. Set the guardrails: Cooldown (minimum minutes between runs) and Max runs / hour (after which Octotor stops and logs an "auto-remediation stopped — manual intervention needed" warning). These prevent a flapping alert from hammering the target or looping forever.
  3. Alert Center > Rules > New/Edit rule → under Run task on fire, pick your alert-triggered task. Save.

Now, whenever that rule fires, the task runs (subject to cooldown/cap). Dispatches, cooldown suppressions, and cap stops are all recorded in the action log (category alert) so you can see exactly when a remediation ran or was throttled. Creating or editing a rule never triggers the task — only real fires do.

Example: a rule that watches a ping target behind a site-to-site VPN can dispatch an On-alert task that SSHes to the firewall and resets the tunnel, then the next poll confirms recovery — with a cooldown so a genuinely-down tunnel isn't reset every poll.

Configuring the guardrails — worked examples

The two guardrails are set on the task (Scheduler ▸ task ▸ Alert-trigger options). They are independent:

FieldWhat it doesRangeDefault
Cooldown (minutes)Minimum time that must pass after a run before the same task runs again. A fire inside the window is suppressed (logged, not run). 0 disables the cooldown.0–144010
Max runs / hourCeiling on runs in any rolling 60-minute window. Once reached, further fires are suppressed and Octotor logs a single "auto-remediation stopped — manual intervention needed" warning. 0 would disable the cap (not recommended).1–1003
Verify after (seconds)Post-fix verification: if the alert that triggered the task is still open this many seconds after the task ran, Octotor runs the task again automatically. Retries bypass the cooldown (a 10-minute cooldown would otherwise swallow a 5-minute verify window) but are bounded by Max retries. Alerts you have acknowledged are never retried — acknowledging means a human owns the incident. 0 turns verification off (the task runs once per fire).0, or 60–864000 (off)
Max retriesHow many extra runs one alert may trigger through verification. The first run doesn't count — 2 means up to 3 runs total per alert. Every re-fire is recorded in the action log (category alert, "remediation re-dispatched").0–102
Worked example — why verification matters. A cloud server behind a site-to-site VPN goes offline at 11:07 and the rule dispatches a "Reset VPN tunnel" task 4 seconds later. But the remote peer is still unreachable at that moment, so the reset achieves nothing — and with verification off, nothing else happens: the server stays down until someone resets the tunnel by hand 23 minutes later. With Verify after 300 and Max retries 2, Octotor re-runs the reset at ~11:12 and ~11:17 while the alert is still open — the second attempt lands after the peer recovers and the tunnel renegotiates on its own.
Example 1 — VPN tunnel reset (steady, safe)

Cooldown 10 · Max/hour 3. The tunnel drops and the ping rule fires:

TimeRule fires?Action
10:00yesRun — reset the tunnel (run #1)
10:02yes (still flapping)Suppressed — inside the 10-min cooldown
10:11yesRun — cooldown elapsed (run #2)
10:22yesRun (run #3)
10:33yesSuppressed — 3 runs already this hour → "auto-remediation stopped, manual intervention needed" alert fires once
11:05yesRun — the rolling hour has cleared the older runs

Net effect: a real reset is attempted, brief flapping doesn't hammer the firewall, and if three resets in an hour don't fix it you get paged instead of an infinite loop.

Example 2 — restart a service, react fast but bounded

Cooldown 2 · Max/hour 6. Good when the remediation is cheap and you want a quick retry cadence but still a ceiling — up to 6 restarts an hour, no more than one every 2 minutes.

Example 3 — one-shot, never repeat automatically

Cooldown 1440 (24 h) · Max/hour 1. The task runs at most once per fire-storm per day — useful for a heavy or disruptive remediation you want to run once and then leave to a human until tomorrow.

Tune by asking two questions: how often is it safe to repeat this action? → sets the Cooldown; and how many attempts before a human must step in? → sets Max runs / hour. All runs and suppressions appear in Investigate (action log, category alert: remediation_dispatched / suppressed_cooldown / suppressed_cap / remediation_capped).

Job History (per task)

Click Hist on any task (or the task name) to open its execution history. Each run lists its jobs with Job# · Asset · Customer · Asset type · Status · Exit · Started · Finished, plus skipped runs (e.g. "no credential available"). Click View on a job to see the full record — the same baseline the Investigate page uses (Triggered-by, Customer, Asset-type, Source/Dst IP) followed by the complete script output. Tasks that span multiple customers show which customer each job belonged to.

System Timezone

All scheduled tasks fire in the timezone set at Settings > Instance > System Timezone. On first save, the dropdown defaults to your browser's timezone (auto-detected) — operators in different regions can override. The Edit Task modal shows the current timezone below the cron expression input so you always know what wall-clock time your tasks will fire at.

Changing the System Timezone takes effect immediately — all enabled cron jobs are re-registered with the new timezone via PostgreSQL LISTEN/NOTIFY (no restart needed).

Cron expression syntax

Cron expressions are five fields, space-separated, each with its own range:

PositionFieldRangeWildcards
1Minute0–59* any, */N every N, A,B,C list, A-B range
2Hour0–23same
3Day of month1–31same
4Month1–12same
5Day of week0–7 (0/7=Sun)same; 1-5 = Mon-Fri

Worked examples

CronFires when
*/5 * * * *Every 5 minutes (00:00, 00:05, 00:10, …)
17 * * * *Every hour at minute 17 (00:17, 01:17, 02:17, …)
0 */4 * * *Every 4 hours starting at midnight (00:00, 04:00, 08:00, …)
3 17 * * *Daily at 17:03
0 6,18 * * *Twice daily — 06:00 and 18:00
0 9-17 * * 1-5Every hour, Mon–Fri 09:00 through 17:00
0 8 * * 1-5Weekdays at 08:00
30 18 * * 5Fridays at 18:30
0 2 * * 0Sundays at 02:00
0 6 1 * *1st day of every month at 06:00
0 6 1,15 * *1st and 15th of every month at 06:00
0 6 1 1,7 *1st of January and 1st of July at 06:00 (semi-annual)

Common gotchas

Creating a Scheduled Task

1. Click [+ New Task] | v 2. Enter: name, description | v 3. Select script from the toolkit picker (grouped by folder) | v 4. Set cron schedule (e.g. "0 6 * * *" = daily at 06:00) | (use Quick Templates dropdown for common patterns) v 5. Optional filters: | Target asset type (computer, switch, firewall...) | Attribute filter (e.g. custom_1 contains "Warehouse") | Customer scope (only assets of a specific customer) v 6. Optional settings: | Credential (auto-select or pick specific) | Run once (auto-disable after first execution) | Auto-proceed if connectivity check fails v 7. Enable ──── task starts running on schedule

Common Cron Patterns

ExpressionMeaning
*/5 * * * *Every 5 minutes
0 * * * *Every hour
0 6 * * *Daily at 06:00
0 8 * * 1-5Weekdays at 08:00
0 0 * * 1Every Monday at midnight
0 6 1 * *First of every month at 06:00

Task Targeting

Tasks can narrow their scope using multiple filters that stack:

FilterEffect
Asset TypeOnly run on assets of this type (e.g. "computer")
Attribute FilterOnly run on assets where a specific attribute contains a value (e.g. location contains "Warehouse")
Customer ScopeOnly run on assets belonging to a specific customer
(none set)Runs on all non-disabled assets

Connectivity Check

Before each job runs, Octotor checks if the target asset is reachable:

For each target asset: | v 1. Ping (ICMP) ──── reachable? ──── proceed to SSH | | | Ping failed | v 2. TCP check SSH port ──── port open? ──── proceed to SSH | (uses SSH Port attribute, | | default port 22) Both failed: | | | ┌────────────┴───────────────┐ | | | | skip_ping_check=ON skip_ping_check=OFF | | | | Auto-proceed to SSH Abort this job | (may still fail) (status: cancelled) v 3. SSH connect + execute script
Auto-proceed if connectivity check fails — enable this for assets where ICMP and TCP probes are blocked but SSH works (e.g. strict firewall rules, port knocking). The job will attempt SSH even if the pre-check fails.

Credential Resolution

For each target asset, the scheduler resolves credentials in this order:

PrioritySource
1Task-level credential (set in task configuration)
2Asset-linked credential (linked in asset detail)
3Per-customer default (set in Settings > Credentials for this customer + OS family)
4Global OS default credential (set as default for the asset's OS family)
5Skip — asset is skipped with a warning in the action log

Task History

Click a task name or the clock button to view its execution history:

A task showing last_status = "ok" with no jobs in history means the task is firing on schedule but finding no eligible targets. Check the History panel for "no_targets" or "skipped" entries to diagnose.

Task Options

OptionEffect
EnabledTask fires on its cron schedule when enabled
Run OnceTask auto-disables after its first execution
Auto-proceed on ping failSkip connectivity check — attempt SSH even if ping and TCP port check fail
Timeout (sec)How long one job of this task may run, in seconds (10 to 3600). Leave it empty to use the global value from Settings > Management Protocols (SSH or WinRM > Timeout (seconds); the SSH default is 30). Set it when the script needs longer than the global value, for example a backup collector that reads a long job history. Every job prints the limit it runs under (Timeout 240 s (the task's "Timeout (sec)" field)) and a job that is cut off says which field set the limit.
Override Identity VerificationWhen checked, the task uses its own identity settings instead of the global ones from Settings > Monitoring. All 5 settings (DNS, SSH gate, require probe, collector check, mismatch behavior) can be configured per task.

Auto-Refresh

The "Auto" button in the page header enables 10-second auto-refresh of the task list. When you trigger a task manually, the list also auto-reloads after 2 and 5 seconds to show the updated status without requiring a manual page refresh.

The Scheduler is admin-only. Scheduled tasks always run with full system permissions regardless of who created them.

Dependencies

Investigate

The Investigation page provides a unified timeline of all system activity — audit logs and job executions merged into a single, filterable view. Use it to answer questions like "what happened to this asset?" or "what failed today?".

How It Works

Investigation merges data from three sources:

SourceWhat it containsBadge color
LogsAudit events — logins, asset changes, config updates, scheduler skips, maintenance actionsGrey
JobsScript executions — queued, running, done, failed, cancelled.Blue
Attribute changesAttribute change history — every old → new transition of every asset attribute, including removals shown as (cleared). Triggered-by tells you whether a human edit (👤), a CSV import (📥) or a collector (📡) made the change.Grey

Attribute changes — investigating history

Pick Source → Attribute changes and two extra filters appear: Attribute (grouped by provider, same as the Assets page) and Value (old or new) — the value filter matches either side of a change, so you can find every place a value ever appeared.

Worked example — "which computers did user production log on to?"
  1. Source → Attribute changes
  2. Attribute → Last Logged-in User (under Computer)
  3. Value (old or new) → production, Customer → tripleorApply

Every row is one transition on one machine — e.g. DOMAIN\alice → DOMAIN\production on TRI-LAB-01 two weeks ago. The View modal shows the full record (old value, new value, change source). You can ask the AI Assist the same question in plain words, or ask Goose: "which computers did user production log on to at tripleor?"

History depth is bounded by the Attribute History Retention setting (Settings → Maintenance, default 180 days), and a deleted asset's history is deleted with it. Searching an exact job number in the search box jumps straight to that job.

The event line

Every row carries a consistent set of fields so any event is scannable at a glance:

FieldWhat it shows
#The row's own id — shown for every event (logs and jobs alike), never blank. Earlier this column only filled for jobs.
Triggered byThe mechanism that initiated the event: 👤 Operator (a human), ⏱ Scheduler (a scheduled task), ⚙ System (autoping / TCP monitor / alert engine / maintenance), 🤖 Goose AI, 🔔 Alert rule, or 🔑 API token.
OperatorThe human behind the event, if any. Blank for pure system / scheduler / alert-rule events. (Available in the View modal; promote to a column via the chooser.)
CustomerWhich customer the event belongs to. Resolved from the event's customer attribution, or from the target asset.
Asset type / Source IP / Dst IPShown when applicable (the event targets an asset / carries a source address). Always present in the View modal; optional columns via the chooser.

Use the Columns ▾ button to promote any of the optional fields (Operator, Asset type, Source IP, Dst IP) into the table. Your selection is remembered in this browser.

Filters

All filters are optional and work together (AND logic). Results update immediately as you change filters. Use the 📌 pin button to persist filters across page navigation.

FilterWhat it does
Date rangeFrom/To date pickers — narrow to a specific time window
SourceShow only Logs, only Jobs, or All
Categoryauth, asset, job, scheduler, settings, sync, goose, ai, identity, maintenance
Statusok, done, error, failed, warning, cancelled, running, queued, identity_mismatch
OperatorUsername of who performed the action (includes "scheduler: task_name")
Asset nameFilter events related to a specific asset
CustomerNarrow to a single customer. The dropdown only lists customers you are scoped to, and the filter narrows within your existing access — it can never widen it.
Search textFree text search across detail, script path, target name

Attribute Cross-Filters

Click Attr Filter to filter events by asset attributes. You can add multiple attribute conditions (AND logic). Each filter has:

Attribute filters only apply to job events (which have an associated asset). Log events without an asset are not affected by attribute filters.

View Details

Click View on any event row to see full details:

AI Investigation

When enabled, the AI query bar appears at the top of the page. Type a natural language question and the AI will:

  1. Translate your question into filter parameters (using knowledge of all asset attributes)
  2. Execute the query (respecting your permissions — customer scope, role, group)
  3. Fetch asset details for mentioned assets (IP, hostname, OS, all attributes)
  4. Summarize the results in plain language

The AI can answer both event questions ("what failed today?") and asset questions ("what is the IP of ast01?") because it has access to asset details during summarization.

Follow-up questions: The AI remembers your previous questions in the current session. You can ask "what jobs ran on it today?" after asking about a specific asset — the AI knows what "it" refers to. Use the Clear button to reset the conversation.

You: "what is the IP of ast01?" | v AI translates → asset_name=ast01 | v Query executes + asset details fetched (IP, hostname, attributes) | v AI Summary: "The IP of ast01 is 192.0.2.20. It is a Linux computer with hostname srv220401." | v You (follow-up): "what jobs ran on it today?" | v AI remembers ast01 from previous context → filters jobs

AI Conversation Logging: When enabled in Settings > AI Configuration, all AI queries and responses are logged to the action log for admin review.

AI Toggle

AI investigation has two levels of control:

LevelWho controlsEffect
Admin settingAdmin (Settings > AI Configuration)Enables/disables AI investigation for all users
User toggleEach user (on the Investigation page)Personal preference to show/hide the AI bar
If the admin disables AI investigation, the user toggle is not shown. Manual filters always work regardless of AI settings.

Export

Click Export CSV to download the current filtered results as a CSV file.

Permissions

RoleWhat they see in Investigation
AdminAll events from all sources, all customers
OperatorEvents for their assigned customers — both logs and jobs
ViewerLog events for their assigned customers only — job events are excluded (no Run page access)

Dependencies

Alert Center

The Alert Center surfaces operational events that need attention — asset outages, identity mismatches, threshold breaches — in a single dedicated page. Unlike the Investigate timeline (full audit log), the Alert Center shows only actionable items and supports acknowledge/resolve workflows.

Evaluation engine

A dedicated container, oct-alert-worker, runs the evaluation loop. It listens on the PostgreSQL channel alert_check and evaluates matching rules on every notification. The worker never calls oct-api over HTTP — all state flows through the database, consistent with the single-responsibility container model (see About).

What fires an alert

The rule engine is live end-to-end. Six rule types can raise alerts — create and scope them in Alerts Admin > Rules:

Watch every data disk on every server

The collect-info scripts report every data disk a server has — not only the system disk. Each disk gets its own attributes (size, free GB, free %, and the volume label), named by its drive letter on Windows (Disk Free D: (%)) or its mount path on Linux (Disk Free /data (%)). Two extra attributes summarize the whole server: Data Disk Free — Worst (%) holds the lowest free-space percentage across all of that server's data disks, and Data Disk With Least Free Space names which disk that is. When a disk is removed from the server, its attributes are cleared on the next collect run. The asset's Overview tab shows one usage line per data disk, fullest first.

This means ONE alert rule covers every data disk on every server. When it fires, the alert text names the disk (for example "disk: D: DATA").

Worked example — alert when any data disk drops below 10% free:
  1. Open Alerts Admin > Rules and click New rule.
  2. Name: Data disk low. Rule type: threshold.
  3. Attribute key: pick Data Disk Free — Worst (%). Operator: <=. Value: 10. Data type: number.
  4. Click Create rule.
Expected outcome: the next time a collect-info run reports any server whose fullest data disk is at 10% free or less, an alert opens in the Alert Center titled "<server>: Threshold crossed: Data disk low", and its detail line names the disk — e.g. attribute=disk_min_pct_free value=1 <= threshold=10 — disk: D: DATA. To watch one specific disk instead, pick its own entry from the same dropdown (e.g. Disk Free D: (%)).

Dependencies

Known issue — implicit offline alerts and explicit status rules: the availability alert above (raised by the per-asset Create alert when this asset goes offline toggle, with no rule behind it) only auto-resolves while no explicit Alert Center status rule covers the asset. If you later add an explicit status rule for that asset type — e.g. a computer / trigger offline rule — any implicit offline alert raised earlier can no longer auto-resolve and must be resolved manually in the Alert Center. New offline events from then on are handled by the explicit rule, which auto-resolves normally. A fix is planned.

Enable offline alerts on an asset

  1. Open the asset on the Assets page and click Edit.
  2. Check Create alert when this asset goes offline and Save.
  3. To enable for many at once: select the assets on the Assets page, then click Enable Alerts in the bulk-action bar (Disable Alerts reverses it).
Worked example — get alerted when a server goes down
  1. Assets page → open DB01Edit → check Create alert when this asset goes offline → Save. (Autoping must already be monitoring DB01.)
  2. DB01 goes down. Within one autoping cycle the Alert Center shows a new critical row — title "DB01 is offline", category availability.
  3. Click the row title → the explain pane shows Status (open), Cause (autoping online→offline at the exact timestamp), and the available Actions (Ack / Resolve).
  4. DB01 comes back → within a cycle the alert auto-resolves with a ✅ "back online" note — no manual cleanup.
Expected outcome: exactly one critical alert for the outage, auto-resolved on recovery. If a Telegram/email channel is attached to a matching status rule, you also get a push on both the offline and the back-online transition.

Acknowledge + resolve

Open alerts show Ack and Resolve buttons per row. Acknowledged means an operator is aware and working on it; Resolved marks the issue closed. Bulk operations (up to 1000 per batch) are available by selecting multiple rows. Viewers can browse the list but cannot ack or resolve.

Storm cap

To prevent flooding the table from misbehaving rules or genuine incidents affecting many assets, the engine caps alert creation at 100 per rule per minute by default. Events above the cap are merged into the first alert of that minute bucket — dedupe_count increments and a STORM badge is shown in the title cell. The cap is operator-configurable in Settings > Alerts > Alert Center (range 1–10000).

Nav badge

A badge next to the Alerts nav link polls /api/alerts/count every 30 seconds and tints by highest-open severity: red for critical, orange for warning, blue for info. It hides when there are no open alerts and caps at 99+.

Settings

Alert notifications can be delivered by email (SMTP), Telegram Bot API, or webhook — configure destinations under Alerts Admin > Channels and attach them to a rule's Notify channels. See Alerts Admin for the end-to-end Telegram walkthrough.

Inline explain row + Enhanced view

Click an alert's title in the table — a second row slides in below it with a friendly explanation of what happened. The inline row has three sections:

The toolbar Enhanced view button (top-right of the page header) toggles a global mode where every alert renders the explain row automatically, no per-row clicking needed. The toggle persists for the lifetime of the browser tab via sessionStorage (closing the tab clears it).

Referring to a specific alert

Each alert row has a clickable #ID in the leftmost column (e.g. #285). Click the ID to:

Paste the URL into chat / email / a ticket — anyone with access to the Alert Center will land on that exact alert with the explain pane already open. The deeplink also clears the status filter so the alert is visible regardless of its current state (open / acknowledged / resolved).

Want to filter the table to a single alert? Type the ID number in the small # id input on the filter bar and press Enter. The page narrows to that one row.

Pin filter

Click the 📌 button in the filter bar to pin your current filter selections. They will persist across page reloads in the same tab — useful for parking a "show me only critical, last 24h, customer X" view while you switch to another page and come back. Pinning matches the existing pattern on the Assets and Investigate pages: pin survives navigation within the tab, dies when you close the tab. Click the Clear button to unpin and reset filters.

Sort columns

Most column headers are clickable. The default sort is Created descending (newest first). Click any sortable header (#, Severity, Category, Title, Asset, Customer, Created, Status) to re-sort by that column descending; click the same header again to toggle ascending. The active column shows (descending) or (ascending); inactive columns show a faint . Sort preference persists for the tab via sessionStorage and is applied server-side so pagination stays correct.

Alerts Admin

The Alerts Admin page (/alerts-admin.html) is the rule-engine configuration surface for the Alert Center. It is admin-only by default; administrators may grant per-group access via Settings > Groups. Write actions (create/edit/delete rules, channels, maintenance windows, globals) remain admin-only regardless of group grant.

Five tabs

Maintenance Windows

Scheduled, time-bounded alert suppression. While a window is active, any rule firing that matches its scope is logged to action_log as rule_suppressed_by_maintenance and no row is inserted into alerts — alerts don't backfill when the window ends.

Scope selection (all three knobs stack — leave all empty for global):

The State column shows whether a window is currently active, upcoming, or past. Upcoming and past windows can be edited freely; editing an active window's end_at in the past ends it immediately.

Asset filter (rule scoping)

Every rule (any rule_type) carries an optional Asset filter that narrows which assets the rule applies to before any per-customer scope or per-asset override is consulted. Two independent knobs (both optional, stacked with AND):

The filter is stored as keys on alert_rules.target_query: type, attr_key, attr_value, attr_mode. It composes with all other axes (per-customer rule scope, per-asset overrides, deduplication, maintenance windows, flap detection, alert dependencies, action chains). Use Preview affected assets inside the rule modal to see exactly which assets the filter selects before saving.

Rule–asset coverage preview

Two complementary lookups answer the question "which rules touch which assets?":

A soft badge on a row means the match depends on runtime conditions the planner can't pre-compute — typically os_event rules (fire only when a matching event arrives) or custom rules with target_query.scheduled_task_id (asset inclusion depends on that scheduled task's own scope). Treat soft matches as "could apply" rather than "will apply."

Configuring a Telegram notification channel

Send Octotor alerts to a Telegram channel end-to-end. Steps 1–4 happen in Telegram / a browser; steps 5–8 happen in Octotor. One bot can serve many channels, and an existing bot can be reused.

#StepWhereWhat you do
1Create a botTelegram → @BotFatherSend /newbot, give a name + a username ending in bot. BotFather returns a bot token like 123456:ABC-DEF… — copy it.
2Create the channelTelegramMenu → New Channel, name it (Private is fine). A group works too.
3Add the bot as adminTelegramChannel name → AdministratorsAdd Admin → pick your bot → enable Post Messages. A bot can only post to a channel it administers.
4Find the chat IDBrowserPost any message in the channel, then open https://api.telegram.org/bot<TOKEN>/getUpdates. In the JSON find "chat":{"id":-100…} — that -100… number is the chat ID. (Public channel? use @channelname directly.)
5Create the channel recordOctotor → Alerts AdminChannels tabClick + New Channel and fill the form (below).
6(Optional) scope to a customerChannels tab → CustomersEmpty = global (all customers). Select customers to restrict.
7Test sendChannels tab → Test sendA test message should land in Telegram within seconds; failures show the reason inline.
8Attach to a ruleRules tab → Edit a ruleIn Notify channels type the channel name (comma-separate multiple, e.g. ops-email, oncall-telegram).

The + New Channel form fields:

FieldValue
NameA short handle the rules reference, e.g. oncall-telegram
Typetelegram
Enabledleave checked
Bot tokenpaste the token from step 1
Chat IDthe -100… number (or @channelname) from step 4
Parse modeleave HTML (default)

The token is encrypted at rest (AES-GCM) and never returned on read — when you edit the channel later, the field shows "(set — leave empty to keep)".

Worked example — an on-call Telegram channel for offline alerts

The token and chat ID below are fabricated placeholders — use the real values BotFather and getUpdates give you.

  1. @BotFather → /newbot → name Octotor Alerts, username acme_octotor_bot → token <BOT_TOKEN> (looks like 111111111:AA…example…).
  2. New private channel ACME On-call; add acme_octotor_bot as admin with Post Messages on.
  3. Post "hi" in the channel, open …/bot<BOT_TOKEN>/getUpdates"chat":{"id":<CHAT_ID>} (a -100… number).
  4. Alerts Admin → Channels → + New Channel: Name oncall-telegram, Type telegram, Bot token <BOT_TOKEN>, Chat ID <CHAT_ID>, Parse mode HTML → Save.
  5. Click Test send"🔔 Test message from Octotor" appears in ACME On-call.
  6. Rules tab → edit the Host offline rule → Notify channels = oncall-telegram → Save.
Expected outcome: next time a host goes offline, the alert posts to the ACME On-call Telegram channel, and auto-resolves with a ✅ message when the host returns.
Network caveat: if your site's egress firewall drops part of Telegram's IP range, a share of sends abort with "This operation was aborted" even though the channel is configured correctly. Retry once before blaming the channel config — a repeatable pattern of aborts is a network-side fix (allow Telegram's API range outbound), not a settings problem. Message content is controlled by the channel's message template — the {{…}} variables ({{alert_severity}}, {{rule_name}}, {{asset_name}}, {{asset_ip}}, {{customer_name}}, {{triggered_by}}, {{asset_type}}, …) are listed under Channels above.

Goose Agent

Goose is an AI-powered IT operations agent that can manage assets, run scripts, investigate events, and execute scheduled recipes — all through natural language chat.

How It Works

Goose runs on the Octotor server and communicates via the MCP (Model Context Protocol). It has access to 43 tools that map to the Octotor REST API:

CategoryToolsWhat they do
Assetsget_asset, list_assets, create_asset, update_asset, search_assets_by_attribute, ping_asset, get_user_link_suggestionsView, manage, and reach IT assets; suggest person→computer links from each machine's primary user
Jobsrun_script, run_command, get_jobs, get_job_output, wait_for_jobExecute scripts/commands on assets via SSH or WinRM
Batch Jobsrun_script_batch, wait_for_jobs, get_job_summariesRun scripts on multiple assets at once, wait for all jobs to complete, and get a summary of results
Eventsget_events, get_attribute_historyQuery the unified events timeline; query attribute change history — who/what changed an asset attribute and when, including removals shown as "(cleared)"
Schedulerget_scheduler_tasks, create_task, toggle_task, get_task_historyManage scheduled tasks and inspect their run history
Systemget_system_status, get_license, get_credentials, get_customers, list_users, list_scripts, get_update_statusSystem health, license state (tier / seat usage / expiry / feature limits), configuration, user directory, toolkit inventory, and software update status (current vs latest published version, last check result)
Identityget_identity, get_identity_historyResolve a person by name/email/UPN to their directory accounts (OU, last logon, group membership, account state); inspect identity-verification event history for an asset
Runbookslist_runbooks, read_runbookRead operational procedures from /opt/octopus/runbooks/
SNMPget_snmp_devices, get_switch_portsList monitored switches and read a switch's port map (MAC/IP per port, link speed, effective status)
Backupget_backup_state, get_backup_sessionsBackup state of protected computers (last success, last result, overdue, days without a successful backup) and the Veeam job run history of a backup server or one computer
Alertsget_alerts, get_alert_countsList alert rules with status and read open/auto-resolved alert counts
Check Pointget_firewall, list_jump_hosts, list_recipes, run_recipe, run_checkpoint_syncInspect Check Point firewalls and jump hosts, list and run CLI recipes, and trigger an account sync
Tool inventory is the authoritative list in api/services/tool-definitions.js. If this table drifts from that file, api/services/tool-definitions.js is the source of truth.

Navigation Safety

In-flight Goose requests survive page navigation. If you start a Goose chat and navigate to another page before the reply arrives, the pending request is persisted to sessionStorage; when you return to the Goose page the reply is re-attached automatically — no lost answers. This applies to both autonomous responses and those held in the Confirmation-Mode pending queue.

Chat Interface

The Goose page provides a chat interface where admins can type natural language requests:

Confirmation Modes

Configured in Settings > AI Configuration > Goose Agent:

ModeBehavior
AutonomousGoose executes all actions without asking
Confirm WritesRead operations are automatic. Write operations (run script, create asset, etc.) require admin approval via the Pending Actions panel
Confirm AllEvery action requires approval

Pending Actions

When confirmation mode is active, Goose's write operations appear as pending actions on the Goose page and dashboard. Admins can approve or reject each action before it executes.

Goose Recipes (Scheduler)

Scheduled tasks can use type "Goose Recipe" instead of a script. The recipe is natural language text that Goose executes on the cron schedule:

Check disk usage on all linux assets for customer TRIPLEOR.
If any disk is below 10% free, run linux/disk-cleanup.sh.
Log results to action_log.

Recipes can reference toolkit scripts by name — Goose knows what's available.

Batch Tools

Goose can run scripts across multiple assets in a single operation using batch tools:

ToolPurpose
run_script_batchExecute a script on multiple assets at once. Accepts a list of asset IDs, a script path, and optional credential. Returns a list of job IDs for tracking
wait_for_jobsWait for a list of jobs to complete. Polls until all jobs reach a terminal state (done, failed, cancelled) or a timeout is reached
get_job_summariesRetrieve a compact summary of multiple jobs — status, exit code, asset name, and truncated output for each job. Useful for reviewing batch results at a glance
Batch tools are especially useful for scheduled recipes that need to run a script on all assets matching a filter and then analyze the results.

Firewalls & CLI appliances

Goose can also work with Check Point / CLI-appliance firewalls and their jump hosts:

Security

Permissions

RoleGoose Access
AdminFull access — chat, approve/reject actions, configure
OperatorNo access (unless group includes 'goose' page)
ViewerNo access

Dependencies

Security & Action Passwords

Octotor provides a layered security system that protects sensitive operations with action passwords and optional TOTP verification. These controls prevent unauthorized changes even if an admin session is compromised.

Action Password System

Action passwords are a separate credential plane from login passwords. When configured, sensitive operations require entering the action password before they can proceed.

LevelWhat it protectsDefault
Master passwordAll operations below (unless overridden)Not set
Per-action passwordsIndividual operations (override master)Inherit from master

Setting the master action password

  1. Go to Settings > Security (admin only).
  2. In Master Action Password, enter a password and confirm it.
  3. Click Set. From now on every protected operation (table below) prompts for this password before it runs.
  4. (Optional) enable Require TOTP with every action prompt to add a second factor — first enrol TOTP for at least one admin (see the lockout rail below).

Configuring a per-action password

  1. In Settings > Security, find the operation in the Per-action list.
  2. Switch its mode from Inherit to Custom.
  3. Type the password for that specific action and click its Set button.
  4. That operation now requires its own password; everything still on Inherit keeps using the master.
Worked example — delegate scheduler control to the on-call team, keep security settings for the senior admin
  1. Settings > Security > set the Master Action Password to Winter-Ops-2026! → Set. All 13 protected operations now inherit it.
  2. Find Create/modify scheduled tasks (action_password_scheduler_create) → mode Custom → password oncall-sched-42 → Set. Repeat for Enable/disable and Run scheduled tasks manually.
  3. Share oncall-sched-42 with the on-call team; keep the master Winter-Ops-2026! with the senior admin only.
Expected outcome: the on-call team can create, toggle and run scheduled tasks using oncall-sched-42, but any attempt to open Access security settings or run a high-risk script still prompts for the master password they don't have — so scheduler work is delegated without handing over the keys to everything.

Protected Operations

#ActionSetting Key
1Import scriptsaction_password_script_import
2Modify scriptsaction_password_script_modify
3Enable/disable scriptsaction_password_script_enable
4Delete scriptsaction_password_script_delete
5Move scripts between foldersaction_password_script_move
6Execute high-risk scriptsaction_password_execution_high
7Execute medium-risk scriptsaction_password_execution_medium
8Run ad-hoc commandsaction_password_adhoc_command
9Create/modify scheduled tasksaction_password_scheduler_create
10Enable/disable scheduled tasksaction_password_scheduler_enable
11Run scheduled tasks manuallyaction_password_scheduler_run_now
12Downgrade script risk levelaction_password_risk_downgrade
13Access security settingsaction_password_settings_security

Per-Action Modes

ModeBehavior
InheritUses the master password (default for all actions)
CustomUses a different password for this specific action
Per-action custom passwords allow delegating specific operations to different team members. For example, the scheduler password could be shared with the on-call team while the security settings password is known only to the senior admin.

TOTP Enforcement

When Require TOTP with every action prompt is enabled (Settings > Security), every action password prompt also requires a valid TOTP code. This provides two-factor protection: something you know (password) + something you have (authenticator app).

Lockout safety rail: Octotor refuses to enable this toggle unless at least one active admin has TOTP enrolled. Without this rail, flipping it on while no admin had TOTP set up would lock everyone out of the very same Settings > Security panel that would flip it back. To enable: enroll TOTP for at least one admin user under Settings > Users → (your row) → "Set up TOTP", then toggle this on.

Rate Limiting & Lockout

ProtectionLimitEffect
Rate limit5 attempts per minute per IPHTTP 429 — try again after the window expires
Lockout10 consecutive failuresHTTP 423 — account locked, requires admin reset or 60-second cooldown

Security Page Gate

The Security section in Settings is protected by a gate. When a master action password is configured, you must enter it (and optionally TOTP) before the security settings become visible. This prevents casual browsing of security configuration.

Changing the Master Password

When a master password is already configured, the Security page shows:

Script Risk Classification

Every script in the Toolkit has a risk level that determines the action password requirements when executing it.

LevelBadgeMeaningAction password
HighHIGHDestructive or privileged operations (disk wipe, user management, service restart)Uses execution_high password
MediumMEDSystem modifications (package install, config change, file write)Uses execution_medium password
LowLOWRead-only or informational (disk check, status query, log review)No password required

Risk levels are assigned automatically by AI validation when a script is saved, and can be adjusted manually by admins. Downgrading a risk level requires the risk_downgrade action password.

Content Hashing

Every script has a SHA-256 content hash that is recalculated when the script is saved. This hash is stored in the database and displayed in the Toolkit detail panel, allowing admins to verify script integrity.

Reset the Octotor admin login password

Use this when nobody can sign in as an administrator — the password was lost, or the only admin account is locked out. It is a host-level recovery procedure: it runs on the server that Octotor is installed on and requires sudo (or docker-group) access there. Anyone who can run it already controls the machine, so it grants no privilege they did not have; guard shell access to the host accordingly. If any administrator can still sign in, do not use this procedure — set the password from Settings ▸ Users & Access instead. The host-level route exists for the case where no administrator session is available, which the web interface cannot resolve by design.

Passwords are stored only as bcrypt hashes, so the old one cannot be read back — it can only be replaced. The procedure below never puts your new password into SQL: it is hashed first, and only the hash reaches the database.

  1. Sign in to the Octotor server over SSH as a user with sudo.
  2. Read the database role and name from the container so the commands below are correct even if this install did not use the defaults. DBU=$(sudo docker exec oct-postgres printenv POSTGRES_USER); DBN=$(sudo docker exec oct-postgres printenv POSTGRES_DB) — on a default install both print octopus.
  3. Confirm the account name. sudo docker exec oct-postgres psql -U "$DBU" -d "$DBN" -c "SELECT id, username, role, active FROM users ORDER BY id" — pick the administrator you want to recover. If the account shows active = f, add active=true to the update in step 5.
  4. Hash the new password. H=$(sudo docker exec oct-api node -e 'console.log(require("bcrypt").hashSync(process.argv[1],12))' 'NewPasswordHere')
  5. Apply it and force a change at next sign-in. sudo docker exec oct-postgres psql -U "$DBU" -d "$DBN" -c "UPDATE users SET password_hash='$H', force_password_change=true WHERE username='admin'" — the command must answer UPDATE 1. UPDATE 0 means the username did not match; re-check step 3.
  6. Verify before you leave the shell. curl -sk -o /dev/null -w '%{http_code}\n' -X POST https://localhost:8443/api/auth/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"NewPasswordHere"}'200 means the reset worked, 401 means it did not.
  7. Clear your shell history (history -c) — step 4 leaves the password in it.
  8. Sign in to the web interface as that user. Because force password change was set, Octotor immediately asks for a new password, so the value you typed in step 4 is only a temporary transit password and is never the one you keep.

Worked example. An install reachable at https://192.0.2.26:8443 has one administrator and nobody knows its password. Step 3 returns 1|admin|admin|t. The operator runs step 4 with 'Transit-9f2Kd!', then step 5, which answers UPDATE 1. Step 6 returns 200. The operator opens https://192.0.2.26:8443, signs in as admin with Transit-9f2Kd!, and is taken straight to the change-password screen, where the real password is set. Total time: under a minute.

Notes: substitute your own port if the install does not use 8443. If the container names differ from oct-api / oct-postgres, list them with sudo docker ps first. The reset is recorded in the database change itself but produces no Investigate entry, so note it in your own change log. If TOTP is enrolled for the account you will still be asked for the code after signing in — clear it with UPDATE users SET totp_enabled=false, totp_secret=NULL WHERE username='admin' in the same session if the authenticator is also lost.

Licensing

Every Octotor install — including Community — runs from a signed license file. Without a valid license the system starts in activation mode: nothing collects, no jobs run, and only the activation page is reachable until a license is applied. The License page (linked from Settings, or reached from the banner's Manage →) is where an admin views status, usage, and features, and applies or replaces a license. Licensing applies instantly — no restart.

Activating a new install

What the License page shows

Seats — how they're counted

A seat is one managed object: a user, a computer, or a managed network device (switch, firewall). Accounts (AD/M365/local directory accounts) and groups do not consume seats. This is why the Assets page can show far more rows than your seat count — it folds in accounts, which the seat meter excludes.

Emergency unlock (break-glass)

If an install is locked out — a license expired mid-renewal, or a disaster-recovery restore landed on a new machine so the license no longer matches — your vendor can issue a one-time unlock code. Paste it under Redeem unlock code (on the activation page or the License page). It grants temporary operation (up to 72 hours) at your licensed tier while a proper license is issued. Each code is single-use and bound to this install.

Worked example — renewing before expiry

Your banner reads "Your license expires in 9 days (2026-08-05)." You email your vendor and receive octotor-acme-2027.oct. On the License page you click Update license, choose the file, and click Apply. The page verifies the signature and that it matches this machine, then swaps it in. The Status card now reads Expires 2027-08-05 · in 365 days, the banner is gone, and no service restarted. If the file had been for a different company's install, the update would have been rejected and your current license left untouched.

Licensing never deletes your data. Removing a license, or letting one expire, stops collection and jobs and makes the system read-only (or activation-only) — your assets, history, and settings remain intact and return the moment a valid license is applied.

Settings

Settings is the admin control center. It covers system configuration, user management, security, credentials, and integrations. Admin access only.

Sections

SectionWhat you configure
InstanceInstance name, default customer, job timeout
CustomersCustomer registry — add/edit/delete customers. Each asset must belong to a customer
Users & AccessCreate/edit/clone/delete users, set roles, manage group memberships, assign customer access, TOTP setup
MonitoringAuto-ping settings (write-on-change), live monitor refresh interval, "Mark asset as Unknown after (seconds)" — how stale an asset's last check may be before Assets/Live Monitor display it as Unknown; 0 = automatic (2× the ping interval). Example: with a 60-second ping interval but a check sweep that takes ~2.5 minutes, the automatic 120-second window makes healthy assets flip to Unknown for part of each cycle — setting 300 stops the flapping (display-only, alerting unaffected). Also: TCP monitoring (SSH/RDP/WinRM port checks), identity verification (DNS, SSH gate, WinRM gate, per-OS default gate, require probe, collector check, mismatch behavior, validity TTL)
Management ProtocolsGlobal enable/disable per protocol (SSH, WinRM), execution defaults per OS family, SSH config (port, timeout), WinRM config (port, timeout, auth method). See WinRM Remote Management section.
AttributesDefine custom asset attribute fields (key, label, type, auto-approve)
CredentialsCreate/edit/delete SSH and WinRM credentials, set OS defaults, per-customer defaults, sudo toggle, assign customer access. Groups can restrict which credentials operators see
GroupsCreate custom permission groups, assign pages/scripts/protocols/credentials
SecurityMaster action password, per-action passwords, TOTP enforcement, script signing (coming soon). See the Security help section for details.
AI ConfigurationLLM status (model name, RAM usage, loaded/unloaded), load/unload model, toggle AI features (script validation, override AI blocks, investigation, conversation logging), Goose agent settings (enable/disable, confirmation mode), Cloud AI Access (enable/disable, provider selection, encrypted-at-rest API key with last-4 preview)
API TokensCreate/revoke Bearer tokens for API and Goose access. Each token has scopes, expiration, IP restriction, rate limit, and activity logging level
NetMapNetwork address translation — map customer LAN subnets to Tailscale-routed subnets. Add, edit, or delete mappings.
MaintenanceDatabase maintenance — data retention policies, scheduled cleanup, backup freshness monitoring, vacuum, DB state monitoring. See Database Maintenance section.

API Tokens — creating and using one

Settings > API Tokens issues Bearer tokens for programmatic access to the same API the web UI uses. Each token carries scopes, an expiry, an optional IP allow-list, a rate limit, and a log level. A token acts as the user who created it and is bound by that user's permissions and customer scope — a token can never do more than its owner. (This feature is licensed: if API Tokens is not in your license the Create button is disabled.)

  1. Create. Settings > API Tokens → + Create Token. Give it a name (e.g. ci-inventory), pick scopes (e.g. read-only assets:read), set an expiry, optionally restrict to an IP. The raw token is shown once — copy it now; it is stored only as a hash and cannot be retrieved again.
  2. Use. Send it as an Authorization: Bearer header:
    curl -H "Authorization: Bearer <your-token>" \
         https://your-octotor/api/assets
  3. Verify / revoke. The token list shows last-used time. Delete a row to revoke instantly (the next request with it returns 401).
Worked example. A read-only monitoring script lists every asset for customer ACME:
TOKEN="oct_live_…"                       # created by an operator scoped to ACME
curl -s -H "Authorization: Bearer $TOKEN" \
     "https://your-octotor/api/assets?customer_name=ACME" | jq '.[].name'
The same token used against a customer the owner can't see returns an empty set (scope is enforced server-side), and a write (POST /api/assets) is rejected unless the token's scopes include it.

Cloud AI API Key (Anthropic)

Settings > AI Configuration > Cloud AI Access stores the cloud LLM API key encrypted at rest in the database (AES-256-GCM with the instance MASTER_KEY — same scheme as credentials and TOTP secrets). The key is decrypted only when oct-api writes the LiteLLM config file at toggle time, and the LiteLLM container reads it from there at startup. The full key never leaves oct-api: the UI shows only the last four characters once configured.

ActionWhat happens
Set / UpdateInline password field appears; entering the key and clicking Save prompts for the master action password. On confirm, the key is encrypted and stored in settings.external_llm_api_key_claude_encrypted; the LiteLLM config file is rewritten with the literal key; oct-litellm restarts to pick it up.
DeleteMaster action password required. The DB row is removed; the LiteLLM config falls back to the legacy EXTERNAL_LLM_API_KEY environment variable (if set in .env) or to no key at all.
ViewStatus row shows Configured: ••••XXXX (last four) or Not set. The full key is never returned by any API endpoint.
Backward compatibility: if no key is configured through the UI, oct-litellm continues to read EXTERNAL_LLM_API_KEY from .env as before. Migrating from the .env-only path is an opt-in that brings DB-dump safety and the lifecycle UI (rotate/delete/audit) above the host-filesystem baseline.
Provider scope: the schema is per-provider (Claude, OpenAI, etc.), but only the Anthropic key field is rendered today. Adding OpenAI later is a UI-only addition — no schema change.

Attribute Updates Flow

1. Mark a script as "Collector" in Toolkit | v 2. Run the collector on an asset (manually or via scheduler) | v 3. Script outputs JSON with key-value pairs | v 4. System matches keys to attribute definitions | ├── Attribute has auto_approve = ON → updated immediately └── Attribute has auto_approve = OFF → goes to pending queue | v 5. Admin reviews in Assets > Pending Attribute Updates | (filter by asset name, bulk approve/reject) v 6. Approved → attribute value updated on the asset Rejected → discarded
Auto Attribute Updates (Settings > Attributes): When turned OFF, collectors still run and output is saved to job history, but no attribute records are created. Use this to disable all automated attribute changes system-wide.

User Setup Flow

1. Create user ──── set username, password, role (admin/operator/viewer) | v 2. Role determines the permission ceiling: | Admin ──── full access, no restrictions | Operator ──── can execute scripts, edit assets | Viewer ──── read-only (dashboard, assets, logs) | v 3. Assign customer access: | All Customers ──── sees all assets (default) | Specific customers ──── sees only those customers' assets | v 4. For operators: add to custom groups to narrow access | Group defines: which pages, scripts, protocols | v 5. Result: user sees only their customers' assets, filtered by their role ceiling and group scope

Credential Setup Flow

1. Create credential ──── label, username, password/key | v 2. Set "Applies To" (linux/windows/mac/all) | v 3. Set customer access: | All Customers ──── credential available for any asset | Specific customers ──── only available for those customers' assets | v 4. Either: | Link to specific asset(s) ──── in Assets > detail > Credentials tab | OR set as default for OS ──── toggle in Settings > Credentials | v 5. Optionally: | Set "Sudo" toggle ──── wraps commands in sudo -S bash -c | Set as per-customer default ──── auto-selected for that customer's assets | v 6. When running a script, Octotor auto-selects the best match: manual > asset-linked > per-customer default > OS default > fail (only credentials matching the asset's customer are considered)

Sudo Support

Credentials can have a Sudo toggle. When enabled, scripts are wrapped in sudo -S bash -c '...' and the credential's password is piped to stdin. This can be overridden per-task in the Scheduler with the Sudo Override setting (force/never/default).

Per-Customer Default Credentials

In Settings > Credentials, you can set a credential as the default for a specific customer + OS family combination. This is useful when different customers have different service accounts.

Effective Permissions

In Settings > Users, click a user to see their Effective Permissions — a summary showing exactly which pages, scripts, credentials, and customers they can access, and which group grants each permission.

CLI Password Reset

If an admin forgets their login password, it can be reset from the host command line:

cd /opt/octopus && ./setup-prod.sh --reset-password

This prompts for a username and new password, then updates the bcrypt hash in the database directly. No login session is required.

To reset the master action password (if locked out):

docker exec oct-postgres psql -U octopus -c "UPDATE settings SET value='' WHERE key='action_password_master';"
Never share admin credentials. Use operator accounts for day-to-day work and create custom groups to control what each team can access.

SNMP Collector — switch port mapping

The SNMP collector (oct-snmp-collector) polls network gear (switches, routers, firewalls) over SNMP and builds a live picture of which device is on which switch port. It never changes anything on the gear — it only reads standard tables (SNMP GET/WALK, no SET). It is on by default — but it only sends packets once SNMP devices are configured: with an empty device list the collector idles and nothing is polled. Turn the master switch off in Settings > SNMP if you never want SNMP traffic from this box.

What it does

  1. Reads the switch MAC table — every MAC address the switch has learned, and which port it was learned on.
  2. Maps computers to ports — when a learned MAC matches a computer asset's LAN MAC (or WiFi MAC), the collector sets that computer's Switch Port and Switch IP attributes, and creates a Connected to relation to the switch (visible in the Relations tab of both assets).
  3. Flags unknowns — a MAC with no matching asset (and a resolved IP) can be surfaced as a discovery-pending device; MACs it can't resolve are counted, not queued.

Reading the "Switch Port" value

The Switch Port attribute stores the raw port description the switch itself reports (the SNMP ifDescr field). It is deliberately kept verbatim so it matches exactly what you'd see in the switch's own management UI — and because the format is vendor-specific, there is no single "clean" form to normalise to:

Vendor / modelExample valueHow to read it
HPE OfficeConnect 18203 Gigabit - LevelPhysical port 3, a Gigabit port. - Level is HPE's fixed description text for the port on this model — it carries no per-port meaning (every port reads "N Gigabit - Level"); the actionable part is the leading number. The switch also exposes a bare ifName of just "3" for the same port.
Cisco IOSGigabitEthernet0/3Port Gi0/3.
GenericPort 12 / eth12Whatever the firmware names the interface.

Rule of thumb: the leading number is the physical port; the rest is the vendor's speed/description text. If a value looks unfamiliar, compare it against the switch's own web UI or CLI (show interfaces description on Cisco) — the collector stores the same string those show.

The switch port map

Open a switch asset to see its Port Map: one row per active port, the MAC(s) learned there, and the asset each MAC resolves to (or unknown if nothing in inventory matches). A port that has learned many MACs is flagged as a likely uplink/trunk — that's normal for the link to a virtualization host or another switch (every downstream device's MAC appears there), so a trunk full of "unknown" entries is expected, not a fault.

Configuring a switch (on the asset)

SNMP monitoring is configured on the switch asset, exactly like SSH/WinRM on a computer — there is no separate device registry. Open the switch on the Assets page:

  1. Create an SNMP credential — v2c (a community string, e.g. public) or v3 (user + auth/priv). Credentials are encrypted at rest under a dedicated key that only the collector can read; the community/keys are never shown back or logged.
  2. On the switch's Credentials tab, link that SNMP credential (the same way you link an SSH or WinRM credential to a computer). The credential carries the SNMP version.
  3. On the Overview tab's SNMP Monitoring card: tick SNMP monitoring enabled, choose the roles — Walk MAC table (an access switch) and/or Walk ARP table (the VLAN's gateway, which maps MAC → IP) — set the port (default 161), and Save. These are stored as switch attributes.
  4. Test (poll now) on the same card confirms the collector can reach the switch and reads its system name.
  5. Turn on the master switch (Settings ▸ SNMP) and (optionally) schedule an SNMP Walk task for periodic polling.

Global settings (Settings > SNMP Collector)

Connection and tuning knobs live under Settings > SNMP Collector: the master enable toggle, default port (161), and the timers — response timeout (per SNMP request), walk deadline (max time for one full table walk), and per-device timeout (max budget per switch per cycle) — plus discovery/event tuning (stability polls, dedup window, caps, MAC-table retention). Poll cadence is not global: how often each switch is polled is set per SNMP Walk task in the Scheduler (schedule-only — the collector never polls on its own timer).

Port status & speed

For each interface the collector reads three standard SNMP dimensions and derives one effective status shown in the Port Map: admin status (ifAdminStatus — is the port enabled?), operational status (ifOperStatus — is the link up?), and spanning-tree state (dot1dStpPortState — is it forwarding, or held blocked by STP?). Speed comes from ifHighSpeed. The effective status is one of: up, down (enabled but no link — e.g. an empty or unplugged port), disabled (admin-shut), blocked (up but STP is holding it), lower-layer-down, absent (no module/SFP), or dormant/testing/unknown. Status is a state, not a fault: an enabled port with nothing plugged in is simply down — it is never flagged as an error just for being down. Some switches don't expose spanning-tree over SNMP — there the status falls back to admin+operational only and never shows blocked.

Port errors are a separate dimension. Whether a port is actually faulting comes from the switch's own error counters (ifInErrors / ifOutErrors), not from up/down state. The Port Map shows a red error count badge on any port that has recorded errors, and the collector flags a port as actively erroring when that counter rises between polls. The switch carries a Ports With Errors attribute (count of ports whose errors rose this poll) — put a threshold Alert rule on it (> 0) to be notified when links start erroring; the alert detail names the offending ports. A change to any port's status or speed, and any port that starts erroring, is written to the event log (Investigate → category snmp_sync). The switch also carries a Ports Down attribute (count of down / lower-layer-down ports) for a "lost ports" threshold alert.

Power over Ethernet. On PoE-capable switches the Port Map shows a per-port PoE badge from the standard POWER-ETHERNET-MIB — delivering (with the negotiated power class), searching, off, or fault. Non-PoE switches simply show no PoE badge.

Vendor differences (why some fields read "unknown")

The collector speaks only standard SNMP MIBs, which keeps it working across vendors without per-model configuration. Two things help it stay accurate on gear from different makers:

Wherever a vendor exposes a value only through its own private MIB (for example the per-device VLAN on an SVL switch, or wireless clients on an access point), Octotor reports it as unknown rather than guessing. A blank field is deliberate — it means the standard tables didn't carry it.

Uplink / trunk ports

A switch's forwarding table tells you which port a device is reachable through, not where it's physically plugged in. An uplink/trunk port that connects to another switch therefore lists every downstream device's MAC — so without special handling all of them would be wrongly mapped to that one port. Octotor treats uplink ports as transit: their devices are still shown in the Port Map (labelled "via uplink (transit)") but are not assigned a switch-port or a "connected to" relation, and never raise a new-device discovery on that switch.

Uplinks are detected two ways: (1) mark them per switch — enter the port numbers in Uplink ports on the switch's SNMP Monitoring card (e.g. 24,53); and (2) auto — any port with more learned MACs than Settings ▸ SNMP Collector ▸ Uplink auto-threshold (default 3, 0 = off). Marking is authoritative; the threshold catches the obvious ones you didn't mark. (Automatic neighbour detection via LLDP is planned.)

To go the other way, use Access ports on the same card: any port number you list there is forced to access and its devices are mapped normally — even if the auto-threshold would otherwise flag it as an uplink. Admin marking wins: Access ports overrides both the threshold and the Uplink ports list, so a port listed as access is never treated as transit.

Speed alerts (sub-gigabit / speed changes)

To alert on slow links, set Min port speed (Mbps) under Settings ▸ SNMP Collector (default 1000 = 1 Gbps). Every poll the collector counts up-ports below that floor into the switch's Ports Below Speed Floor attribute — create a threshold Alert rule on it (> 0) to be notified when any port negotiates below 1 Gbps (e.g. a bad cable dropping a gigabit link to 100 Mbps). To alert on any speed change, the switch also carries a Ports Speed-Changed last poll attribute (count of ports whose speed differed from the previous poll; it resets to 0 on the next clean poll) — a threshold rule > 0 fires on a change and auto-resolves. To create either: Alerts ▸ + New Rule ▸ type = threshold ▸ pick the attribute ▸ operator > ▸ value 0 ▸ choose a notification channel.

New-device alerts

When the collector sees a stable, IP-resolved MAC that isn't a known asset, it queues it for discovery and writes a device_discovered event (Investigate → snmp_sync). The switch also carries a Pending Devices attribute (count of not-yet-approved devices found via that switch) — put a threshold Alert rule on it (e.g. > 0) to be notified when unknown gear appears on the network.

Switch & device network details

Each SNMP poll now also records the switch's own Switch MAC, Subnet Mask and Default Gateway (Overview → Attributes). For every device the collector maps to a switch port it records the Connected Switch IP (the mgmt IP of the switch it's plugged into) and the Switch Port Speed (the port's negotiated speed) alongside the existing Switch Port / VLAN. Windows and Linux collectors also fill each computer's Default Gateway, Subnet Mask and DNS Servers ("ipconfig /all"-style). Collector-written attributes carry the AUTO provenance badge (not MANUAL).

Switch-to-switch topology

When two managed switches are linked, the collector records a Connected to relation between them (Overview → Relations), so you can see which switch uplinks to which. It's detected from LLDP neighbours when the switches expose it, and falls back to matching a switch's own MAC in another switch's forwarding table. (This complements uplink/access marking — it identifies the neighbour, not just that a port is a trunk.)

Finding a port on the Port Map

Use the search box on a switch's Port Map tab to filter rows by port number, port name, connected asset name, MAC or IP — handy on a busy 48-port switch. Speed/uplink labels and the transit markers are preserved while filtering.

Scheduled SNMP-Walk history

A scheduled SNMP Walk task now shows its runs in the task History panel (triggered / nothing-to-poll / skipped / rejected), even though an SNMP poll doesn't create a job the way a script task does.

Roles — why two kinds

A plain L2 switch knows MAC → port but not IP addresses. To resolve MAC → IP (needed to auto-assign an unknown device to a customer by subnet), the collector reads the ARP table of the VLAN's L3 gateway (a router, L3 switch, or firewall). This is standard, routed SNMP — the collector works across VLANs and remote sites; it never needs to sit on the same network segment as the device.

Ask the AI assistant

The Goose AI assistant can query SNMP data directly through two read-only tools: get_snmp_devices (list the monitored switches and their poll state) and get_switch_ports (read a switch's full port map — MAC/IP per port, link speed, effective status). Ask e.g. "which ports are down on SW-CORE-01?" or "what's plugged into the core switch?"

Security

Licence

The SNMP collector is a Pro-tier feature.

Backup Collector — Veeam backup state on your assets

The backup collector reads the job history of a Veeam Backup & Replication server and writes the result onto the computers that server protects. On each protected computer you see when its last successful backup ran and how the last run ended. On the backup server you see how many jobs succeeded, ended with warnings, failed, or are overdue, and how many failed recently. Six alert rules fire when a backup fails (now, or at any time in the last 24 hours), ends with a warning, or is late. The collector script windows/veeam-vbr-collect.ps1 only reads from Veeam: it never starts, stops, edits, or deletes a backup job. This feature needs a Pro or Enterprise license.

What the collector writes

On every computer it matched to a backed-up item (the BACKUP group in the Attributes tab):

AttributeMeaning
Backup — Last Successful RunThe newest restore point Veeam holds for this computer, or the end time of the newest job run that ended with Success. This value is never cleared.
Backup — Last ResultHow the newest job run ended: Success, Warning, Failed, or Not protected (the computer is no longer in any backup job on that server).
Backup Job NameThe Veeam job or jobs that include this computer.
Backup ServerThe Veeam server that reported it.
Backup OverdueYes when the last successful run is older than the job's own schedule allows (schedule interval times 1.25, or 30 hours when the schedule cannot be read).
Backup Copy — Last Successful RunThe newest successful backup copy run, when a copy job exists.
Backup — Last Warning or Error MessageThe last warning or error lines of the newest run that ended with Warning or Failed. Empty after a Success.

On the backup server itself: Backup Product, Backup Data — Last Collected, the job counts (Backup Jobs — Total / Successful / With Warnings / Failed / No Last Result / Disabled / Overdue) with a matching list of job names for each count, Backup Jobs — Failed Recently (with its list of names) for every job that had a failed run in the last 24 hours, or since the previous collection when that is longer ago, even if that job is idle or green again by now, and Backed-Up Items With No Asset for items Veeam protects that Octotor could not match to an asset.

Why two failed counts? Backup Jobs — Failed looks at each job's newest run only. A backup copy job that fails at 03:00 and is idle again when the collector runs at 06:30 reads as "No Last Result", so that count stays 0. Backup Jobs — Failed Recently looks at every run that ended in the last 24 hours (or since the previous collection, when the collector was paused for longer than that), so the 03:00 failure is counted, the rule Backup job failed recently on server fires, and the count returns to 0 once the failure is older than 24 hours and nothing else failed. Running the collector again a few minutes later reports the same value.

Set it up step by step

  1. Mark the backup server. Open Assets, open the Veeam server, go to the Attributes tab, and set Veeam Backup Server to Yes. The collector task targets this attribute, so only servers you mark are ever queried.
  2. Give Octotor a Windows credential for that server. On the same asset, open the Credentials tab and link a Windows account. The account must be allowed to load the Veeam PowerShell module; the Veeam role Backup Viewer is enough, because the script only reads. Veeam 13 or newer: the backup server must also have PowerShell 7 installed (Microsoft's free download, https://aka.ms/powershell), because Veeam 13 provides its PowerShell module for PowerShell 7 only. The collector checks the installed Veeam version, runs its Veeam part in PowerShell 7 when the version is 13 or newer, and tells you in plain words when PowerShell 7 is missing. Veeam 12 and older need nothing extra.
  3. Schedule the collector. Open Scheduler and click + New task. Script: windows/veeam-vbr-collect.ps1. Target: an attribute filter Veeam Backup Server equals true. Schedule: once a day after the backup window ends, for example 30 6 * * * (06:30 every day). In the Execution section set Timeout (sec) to 240: the collector reads the whole job history and a server with many jobs needs more than the 30-second SSH default (a cut-off run shows SSH connection timed out after 30 s and names the field to raise). The same applies to a server reached over WinRM: a run started from the Run page uses the Timeout field of that page (or the WinRM default of 180 seconds when the field is empty), so give a manual run at least 240 seconds too. A server with a long job history needs more: on a Veeam 13 server with 8 jobs and about 1,500 recorded runs the collector took 9½ minutes, so set Timeout (sec) to 900 when the task covers such servers (a cut-off run shows timed out after N s and names the field to raise). Save the task.
  4. Check the first run. After the task has run, open the backup server asset. The Backup jobs tab lists every job run and every backed-up item with its result. The Attributes tab shows the BACKUP group with the counts. Open one of the protected computers: its BACKUP group now carries its own values.
  5. Alerts. Six rules are seeded and enabled under Alerts: Backup job failed on server (critical), Backup job failed recently on server (critical — fires for a job that failed in the last 24 hours, or since the previous collection when that is longer ago, even when that job is idle or green again by the time the collector runs), Last backup of this computer failed (critical), Backup overdue for this computer (warning), Backup job warning on server (warning), Last backup of this computer ended with a warning (warning). Threshold rules only fire for assets whose Threshold alert switch (asset Overview ▸ Alerts card) is on; by default the collector switches it on for every computer it matches and for the backup server (see the setting below).

Settings

Worked example

The backup server BK-01 at customer Tripleor runs three Veeam jobs. The file server FS-02 is in the job 02 File servers, which runs every night at 01:00. You marked BK-01 as a Veeam Backup Server, linked its Windows credential, and scheduled the collector for 06:30 daily.

  1. On 8 September the 01:00 run of 02 File servers ends with Failed because the file server was unreachable.
  2. At 06:30 the collector runs on BK-01. On FS-02 it writes Backup — Last Result = Failed, Backup — Last Warning or Error Message = "Failed to connect to FS-02: The network path was not found", and leaves Backup — Last Successful Run at 7 September 01:12 (the last good restore point). On BK-01 it writes Backup Jobs — Failed = 1 and Backup Jobs — Failed (names) = "02 File servers".
  3. The rule Last backup of this computer failed raises a critical alert on FS-02, and Backup job failed on server raises one on BK-01 naming the failed job. The error text itself sits in FS-02's Backup — Last Warning or Error Message attribute. Your notification channels deliver both.
  4. You open BK-01 ▸ Backup jobs, click Failed, and open the Log of the FS-02 row to read the Veeam log lines. View job output opens the Jobs tab with the full collector output.
  5. You fix the network issue and re-run the job in Veeam. After the next collector run at 06:30 the next day, FS-02 shows Backup — Last Result = Success and a new Backup — Last Successful Run; the message attribute is emptied; both alerts resolve.

When a run fails, or finishes but lands no backup data

A collector run that cannot read Veeam still ends as a green job (exit code 0) — the reason is inside its output, not in the job status. Open the backup server asset, go to the Jobs tab, and open the collector run; the errors list at the end of its output says what happened. The same message is recorded in Investigate (category backup, "nothing landed"). The messages you can see:

Ask the AI assistant

The Goose AI assistant reads the same data through two read-only tools: get_backup_state (which computers are protected, their last result, and how many days since a successful backup) and get_backup_sessions (the job run history of a backup server or of one computer, with the log lines of failed items). Ask for example "Which computers at Tripleor have no successful backup in the last 2 days?" or "Why did the backup of FS-02 fail last night?"

How computers are matched

Each backed-up item is matched by name to a computer asset of the same customer as the backup server: the item name, the DNS name Veeam recorded for the guest on its newest restore point, and the short form of either. The DNS name is what makes a virtual machine match when its name in the hypervisor differs from its host name (for example a machine named FS-02_replica in Hyper-V whose Windows host name is FS-02: it lands on the asset FS-02). When two Veeam items map to the same asset, the newest backup wins and the job names are combined. If two assets could match the same item, nothing is written to either of them and the item is listed under Backed-Up Items With No Asset with the note "(ambiguous)". Items with no matching asset are listed there too, so you can create or rename the asset and the next run will match it.

Reading the Backup jobs tab

Each row is one job run, or one backed-up item inside a run: the job name on the first line and Backed-up item: name under it. On a computer's own Backup jobs tab the item line is left out, because the item is that computer; a backup job and its backup copy job appear as two rows per day, told apart by the Type column. The result chip shows the item's own result when it has one, otherwise the job's. Log expands the last log lines Veeam recorded for a failed or warning item. The filter chips re-read the rows from the server. The tab is hidden on installs without a Pro or Enterprise license and on switches, persons, and account assets.

M365 Connectors

M365 Connectors sync Microsoft 365 / Entra objects into Octotor straight from the platform — no Windows box or on-prem agent. Phase 1 collects users, license assignment, MFA registration status, and Entra-joined devices; they land through the same discovery pipeline as AD collection (auto-approve or pending queue per the discovery_auto_approve setting). One connector per customer.

Master switch

Settings > M365 Connectors > Collector master switch (default Disabled). While disabled the collector container makes no outbound connections at all — connectors stay stored but inert, scheduled syncs skip, and Test connection refuses to run. Certificate generation still works while disabled (it is local-only crypto). Enable the switch only when you are ready for Octotor to talk to Microsoft's endpoints (login.microsoftonline.com and graph.microsoft.com — nothing else).

How authentication works

Octotor uses certificate-based app-only Graph access (Microsoft's recommended pattern for unattended services). Octotor generates an RSA-3072 keypair per connector; the private key never leaves Octotor and is stored encrypted under a dedicated key that only the collector container holds. You upload the public certificate to your Entra app registration. At sync time the collector signs a short-lived JWT with the private key and exchanges it for a Graph token — no client secrets, nothing to rotate in two places.

Setting up a connector — step by step

One-time per tenant, ~10 minutes. Steps 1–2 and 5 happen in the Microsoft Entra portal and require a Global Administrator of the customer tenant; steps 3–4 and 6 happen in Octotor. (This walkthrough was validated live against a real tenant on 2026-07-03.)

Part 1 — register the application (Entra portal)

  1. Sign in to entra.microsoft.com with a Global Administrator account of the tenant you are connecting.
  2. Go to Identity > Applications > App registrations > + New registration.
  3. Name: e.g. Octotor Collector (any name works). Supported account types: "Accounts in this organizational directory only" (single tenant). Redirect URI: leave empty. Click Register.
  4. On the app's Overview page, copy two values for step 4: Directory (tenant) ID and Application (client) ID. Neither is a secret — they are identifiers.

Part 2 — grant read-only Graph permissions

  1. In the app: API permissions > + Add a permission > Microsoft Graph > Application permissions (NOT Delegated).
  2. Tick the base set: User.Read.All, Organization.Read.All, Device.Read.All. Then the optional set — each unlocks extra data and degrades gracefully (sync still works, the sync summary notes what's missing):
    • AuditLog.Read.All — MFA Status/Methods, per-user last sign-in, and the sign-in events feed (Part 7; sign-ins additionally require an Entra ID P1/P2 tenant)
    • Reports.Read.All — mailbox size (usage reports)
    • GroupMember.Read.All — group membership (all group types, Teams tagged)
    • RoleManagement.Read.Directory — activated directory roles per user (Global Administrator, …)
    • MailboxSettings.Read — mail-forwarding visibility: user-created inbox rules that forward/redirect mail (classic BEC indicator). Detects rule-based forwarding only — the admin-set "Email forwarding" mailbox property is a separate Exchange API and is not covered.
    Click Add permissions.
  3. Click "Grant admin consent for <tenant>" and confirm — the Status column must show green checks on every row. This is the step that requires the Global Admin role; without consent, Test fails with a consent error.

Part 3 — create the connector and its certificate (Octotor)

  1. Open Settings > M365 Connectors > Add connector: pick the customer this tenant belongs to, paste the Tenant ID and Application (client) ID from Part 1, click Create + generate certificate.
  2. The CERT pill turns ready within a few seconds (Octotor generates an RSA-3072 keypair; the private key is stored encrypted and never leaves the collector). If the pill stays pending (typically: the connector was created before the collector container was started) or shows error, click the row's Regenerate button to re-request key generation — the collector also retries all stuck-pending connectors automatically on startup. Example: on a fresh install where COMPOSE_PROFILES=m365 was set after the connector was created, the pill stays pending; start the collector container and it turns ready on boot without any clicks.
  3. Click Cert on the row — it downloads octotor-m365-connector-<id>.cer (the public half only; there is no private-key download).

Part 4 — upload the certificate to Entra

  1. In the Entra app: Certificates & secrets > Certificates tab > Upload certificate, pick the downloaded .cer file, add.
  2. Sanity check: the thumbprint Entra now shows must equal the THUMBPRINT on the connector row in Octotor. If they differ you uploaded the wrong file.

Part 5 — test and sync

  1. In Octotor: set the Collector master switch to Enabled and Save (until now the collector has made no outbound connection at all).
  2. Click Test on the connector row. Expected: the TEST pill goes pendingok within a few seconds; hovering the row shows your organization name (Octotor acquired a token AND read /v1.0/organization — proving both the cert upload and the admin consent).
  3. Sync runs land discovered users and Entra devices in the Assets > Discovery panel (or create them directly when Auto-approve discovered identities is on). Scheduled/recurring sync is configured in the Scheduler ("M365 Sync" task type).
  4. On a synced device, the Entra values (OS, compliance, join type, account state, last seen, MDM enrollment, owner) live on a Microsoft 365 device account linked to the computer — exactly like an AD machine account, and like user accounts. The computer's Attributes tab shows the account as a "Microsoft 365 · read-only · collected" card; deleting the account asset removes the collected Entra data. A hybrid-joined machine carries both its AD machine account and its M365 device account on the same computer. The registered owner also links the device to its person (like AD's assigned-to).
  5. Collected per user account: state, licenses, MFA status + methods, last sign-in, account created, last password change, usage location, office, employee ID, company, manager, mailbox size, group membership (all group types; Teams-backed groups tagged (team)), directory roles (activated assignments — Global Administrator etc.), and mail forwarding (enabled inbox rules that forward/redirect, shown as rule → address; None when the mailbox has no such rule — so a rule appearing or disappearing is a change an alert rule can fire on). Mailbox size needs Reports.Read.All; groups need GroupMember.Read.All; last sign-in needs AuditLog.Read.All + Entra P1/P2; roles need RoleManagement.Read.Directory; mail forwarding needs MailboxSettings.Read — each degrades gracefully with a note in the sync summary when missing.

Part 6 — schedule recurring syncs

  1. Open Scheduler and click + New task.
  2. Set Task type to M365 Sync — the asset/script sections disappear and an M365 connector picker appears (connectors themselves are managed by admins in Settings > M365 Connectors).
  3. Pick the connector, name the task (e.g. m365-nightly), choose a schedule (nightly is typical — a sync only writes attribute changes), Save.
  4. Run Now on the task row triggers an immediate sync. Dispatch is fire-and-forget: the task history shows the dispatch (ok / skipped / error), and the sync's own result (sweep_completed / sweep_failed with counts) appears in Investigate under the m365_sync category.
  5. When the collector master switch is OFF, scheduled syncs are skipped (visible in the task history) — nothing runs and no outbound connection is made.

Part 7 — sign-in events + alerts

Each sync also collects sign-in events (successes and failures) from Entra's auditLogs/signIns — requires AuditLog.Read.All (application) and an Entra ID P1/P2 tenant; without them this phase is skipped with a note in the sync summary, and the user/device phases still run.

Troubleshooting

Statuses

ColumnValues
CERTpending (generating) → ready · error (hover the row for the reason)
TESTnot run · pending (dispatched to the collector) · ok · error (hover for the redacted reason)
STATEEnabled / Disabled — per-connector switch, independent of the master switch

Security notes

Sync scheduling (recurring collection) arrives with the Scheduler task type M365 Sync — see the Scheduler section once available.

How Permissions Work

Octotor uses a layered permission system. Each layer narrows what a user can see and do.

Customer filter (which assets the user can see) → Role ceiling (what actions are possible) → Group scope (which scripts, pages, protocols)

Layer 1: Customer (Asset Visibility)

Each user is assigned to specific customers or "All Customers". This is the outermost filter — a user can never see assets belonging to a customer they're not assigned to.

SettingEffect
All CustomersUser sees all assets from every customer (default)
Specific customersUser sees only assets belonging to their assigned customers
Admin users always have access to all customers regardless of assignment. Customer scoping applies to assets, credentials, job history, and logs.

Layer 2: Role (Ceiling)

Every user has one role. The role defines the maximum actions possible.

RoleCan doCannot do
AdminEverythingNothing restricted
OperatorView assets, execute scripts, view logs, run protocolsManage settings, manage users, delete assets, manage credentials, manage scheduler
ViewerView dashboard, view assets, view own logs, investigate (logs only)Everything else — job events hidden in Investigation

Layer 3: Groups (Scope)

Custom groups narrow an operator's access. Without any custom group, an operator sees everything their role allows. With a custom group, they only see what the group grants.

Operator + no custom group = full operator access (all scripts, all pages) | v Operator + custom group "Server Admins" = only sees: Pages: [run, assets, logs] Scripts: [linux/*, custom/server-admins/*] Protocols: [Server Onboarding, Emergency Restart]

Multiple Groups

A user can be in multiple custom groups. Their effective permissions are the union of all groups.

Group "Server Admins": scripts = [linux/*] pages = [run, assets, logs] Group "Network Team": scripts = [network/*] pages = [run, assets] ───────────────────────────────────────────── User in both: scripts = [linux/*, network/*] pages = [run, assets, logs]

Built-in Groups

GroupAuto-assigned toEditable?
AdminsAdmin usersNo — admins bypass all checks
OperatorsOperator usersNo — use custom groups to narrow
ViewersViewer usersNo — viewer permissions are fixed

Permission Change Effect

Permission changes take effect within 30 seconds — no logout/login required. The system checks permissions on every API request using a short-lived cache.

To restrict an operator: create a custom group with only the pages and scripts you want them to access, then add the operator to that group. Their access is immediately narrowed.

Network Address Translation (NetMap)

When managing assets across multiple customer sites, IP address ranges can overlap. For example, two customers may both use 192.168.0.0/24 on their LANs. To avoid conflicts when routing through Tailscale, subnet routers use SNAT to remap one customer's range to a unique range.

NetMap tells Octotor which IP translations to apply per customer, so ping and SSH reach the correct Tailscale-routed address. Asset IPs in the database always remain the real LAN addresses — translation happens at runtime only.

Example

CustomerTRIPLEOR
Original Network192.168.0.0/16 (their real LAN)
Mapped Network10.99.0.0/16 (Tailscale SNAT'd)
Asset stored IP192.168.0.10
Octotor pings10.99.0.10 (translated)

Key Rules

Adding a mapping

  1. Go to Settings > NetMap (admin only).
  2. Click + Add mapping and pick the Customer.
  3. Enter the Original network — the customer's real LAN CIDR, e.g. 192.168.0.0/16.
  4. Enter the Mapped network — the Tailscale-SNAT'd CIDR, e.g. 10.99.0.0/16. It must have the same prefix length as the original.
  5. Click Save. Within ~60s (the rule-cache TTL) every ping / SSH / WinRM / SNMP to that customer's assets connects to the translated address, while the stored/displayed IP stays the real LAN one.
Verify with a ping from the asset's drawer: the asset shows 192.168.0.10 but the job connects to 10.99.0.10. If a probe still hits the real IP, the mapping's prefix length probably doesn't match the original.

CLI Appliances (Check Point, Cisco, …)

Some devices — firewalls, switches, routers — aren't managed by installing an agent or running an ordinary shell script. You drive them through their own command-line interface: you type a command, the device shows a menu or asks a question, you answer, and so on. Octotor supports these devices as CLI appliances, and automates those back-and-forth sessions with recipes.

Pro feature. CLI-appliance recipes require a Pro license.

1. Add the device as a CLI appliance

On the Assets page add the device (e.g. a Check Point firewall) and set its OS family to CLI appliance (Check Point / Cisco). This tells Octotor to connect over SSH, skip the operating-system probes it runs on servers, and never try sudo. Add a Vendor attribute (Check Point Quantum Spark, Cisco IOS, …) so the right recipes are offered for that device. Attach an SSH credential just like any other asset.

2. Pick an action and fill in the plain-language form

On the Run page, choose the device and then an action (a recipe). Each action shows a short description and, if it needs any input, a form with clearly-labelled fields — for example “VPN peer gateway (IP address)” with a hint and an example value. There is no code or command syntax to learn; you fill in what the field asks for and press Run. Octotor validates the input (an IP field must be a real IP, etc.) before anything is sent to the device.

3. What happens under the hood

Interactive actions run in a dedicated, isolated worker container (oct-recipe-worker) so a session that stalls on one device can never affect ordinary SSH/WinRM jobs. The worker opens an SSH session, walks the device's menus step by step (waiting for each prompt before it types the next answer), captures the full transcript into the job output, and enforces safety limits: per-step and overall timeouts, and a guard that refuses to send dangerous inputs (for example, a Check Point reset-all-tunnels menu option is blocked; only the specific peer you chose is reset).

Seeded example: Check Point site-to-site VPN

Scheduling & automatic reset (end-to-end)

The same action can run on a schedule or be fired automatically by an alert:

Because the recipe's parameters are stored on the task, the automation always knows exactly which peer to act on.

Collecting firewall inventory (Check Point)

Beyond running recipes, Octotor can pull a full inventory snapshot from a Check Point Quantum Spark firewall. Run the System info action (or schedule a Check Point sync) and the device's Overview fills in with its model, software version, serial number, license expiry, uptime, and — rendered as sortable tables on the device panel:

Check Point Quantum Spark (embedded Gaia) exposes some of this only through the VPN blade, not the main configuration dump — Octotor reads each value from the right place for you, so the firewall's Overview is complete.

Syncing firewall users & hosts into Assets

A Check Point sync reads the firewall's local user objects and host objects and files them into Assets exactly the way the Active Directory and Microsoft 365 connectors do: each object is matched to an existing person or computer (or proposed for approval in the discovery queue), then linked as an account under that owner. Host objects become device-side accounts and fold into Devices; user objects become user-side accounts and fold into Users — see User Assets & Accounts. The sync honours the same auto-approve setting as AD and M365, so on a strict tenant unmatched objects wait for you in the discovery queue.

To run it on a schedule, create a Scheduler task with Task type = Check Point Sync. The Target section picks the firewalls: choose a specific firewall for one device, or set Asset type = firewall to sync every firewall in scope — optionally narrowed by the Customer dropdown. Worked example: task ALL-FW-NIGHTLY, Task type Check Point Sync, Customer Any customer, Asset type firewall, no specific asset, cron 0 3 * * * — every enabled firewall (or CLI appliance) across all customers syncs its users and hosts at 03:00, one sync per firewall, and each run appears separately in Investigate under the checkpoint_sync category.

Enterprise feature. Running recipes on a CLI appliance is a Pro feature, but the Check Point account sync (this section) requires an Enterprise license.

Jump Hosts — reaching a device through a bastion

Some devices are not reachable directly from the Octotor server — you first SSH into a jump host (bastion) on the customer's network and from there to the device. Configure this once under Settings ▸ Jump Hosts: give the jump host its address and an SSH credential. Then, on the device's Assets ▸ Overview ▸ Edit, pick that Jump Host from the dropdown. From then on every SSH connection to that asset goes through the bastion: appliance recipes, the Check Point account sync, and ordinary SSH jobs of any kind — scripts, commands, collectors and scheduled tasks — on any device type, Windows hosts with OpenSSH included. The device's own credential is used on the second hop. The job output shows the line Routing via jump host user@host:port (SSH).

Worked example: a Veeam backup server BK-1040 at a customer site is only reachable through that site's Linux bastion site-jump (192.0.2.10). Create a credential for the bastion login, add a jump host named site-jump with that address and credential under the customer, then open BK-1040 ▸ Overview ▸ Edit and set Jump Host = site-jump. The scheduled backup collector task for BK-1040 now runs over SSH through site-jump; its job output starts with the routing line, then Checking SSH port 22 on 192.168.10.5 via the jump host…, then the script output.

Credentials for CLI appliances

When you create the SSH credential for an appliance (or its jump host), set Applies to = CLI appliance. Credentials carry a name of their own — the Assets ▸ Credentials panel lists them by that name, with the login shown on a separate Login: line, so two credentials that share a username (say two admin accounts on different firewalls) stay easy to tell apart.

License Agreement

Download .md

Octotor is distributed under a Source-Available license. The Licensee may inspect the source installed on a Licensed Instance for the Licensee's internal purposes — including security review, understanding how the Software operates, compliance verification, and reporting bugs — while reuse, redistribution, and competing products are not permitted. Octotor and the Octotor logo are trade names and unregistered trademarks of Caprior. Governing law: State of Israel.

Loading license text…

About Octotor

Version
Build Date
Instance Name
Instance ID
Your Username
Your Role

WinRM Remote Management

Octotor supports Windows Remote Management (WinRM) as an alternative to SSH for executing commands on Windows machines. WinRM uses PowerShell remoting over HTTP (port 5985) with NTLM authentication.

Windows Target Requirements

Run the following on each Windows target (as Administrator):

Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $true
Set-Item WSMan:\localhost\Service\Auth\Basic -Value $true

WinRM must be enabled and the firewall must allow port 5985 (HTTP). HTTPS (port 5986) and Kerberos authentication are planned for a future release.

Protocol Resolution

Octotor determines which protocol (SSH or WinRM) to use for each asset using this hierarchy:

#PrioritySource
1Run Command overrideOne-time selection in the Run Command modal (not saved)
2Task force protocolScheduled task configuration: Auto / Force SSH / Force WinRM
3Asset overridePer-asset Connection Protocol field in the asset edit modal
4Global defaultSettings > Management Protocols > Execution Defaults (per OS family)
5FallbackSSH (always available)

If WinRM is resolved but globally disabled (Settings > Management Protocols > WinRM = Disabled), the worker automatically falls back to SSH and logs a warning.

Execution Flow

1. Resolve Protocol (global enable → override → asset → default → ssh) | 2. ICMP Ping (same for both protocols) | 3. TCP Port Check (SSH: port 22 | WinRM: port 5985) | 4. Identity Gate (SSH probe or WinRM probe, per OS default) | 5. Execute (SSH: ssh2 library | WinRM: pwsh → Invoke-Command)

Identity Verification

WinRM supports hostname identity verification, similar to the SSH gate. When enabled, Octotor connects via WinRM and runs [System.Net.Dns]::GetHostName() to verify the remote machine's identity before executing the actual command. Configure the default gate per OS family in Settings > Monitoring > Identity Verification.

Management Protocols Settings

#SettingDescription
1Protocol Status (SSH/WinRM)Global enable/disable. Disabled protocols are hidden from all UI and the worker falls back to SSH.
2Execution DefaultsDefault protocol per OS family (Windows: WinRM or SSH, Linux: SSH)
3WinRM Default PortDefault: 5985. Override per-asset via the WinRM Port attribute.
4WinRM TimeoutConnection timeout in seconds. Default: 30.
5WinRM Auth MethodCurrently NTLM (Negotiate). Kerberos and Certificate planned for future.

Services Status

Overview

The Services page provides a live dashboard of system health. It is available to administrators only and auto-refreshes every 10 seconds.

Sections

#SectionDescription
1HostCPU load averages, memory usage, database size, and backup freshness indicator.
2ContainersAll running oct-* containers with status, uptime, CPU %, memory, and restart count. Requires Docker socket mount (configured automatically by the installer).
3Service HeartbeatsBackground services (autoping, tcpmonitor, scheduler, maintenance) report their last heartbeat. A "stale" indicator appears if no heartbeat is received for over 2 minutes.
4Active WorkCount of currently running and queued jobs.
5Recent ErrorsLast 10 error entries from the action log.
6Service ControlsPlaceholder for stop/start/restart controls — planned for a future release.

Architecture

Background services run in a dedicated oct-worker container, separate from the API. This isolation means a stuck background task cannot affect API responsiveness. Services communicate via the database (LISTEN/NOTIFY) and write heartbeats to the service_heartbeats table so the API can report their health.

Worked example — a job stuck "queued" turns out to be a dead worker
  1. An operator reports a whoami job sitting in queued for 20 minutes. You open Services.
  2. Service Heartbeats shows ssh-worker with a stale flag — last heartbeat 18 minutes ago (healthy services update every ≤30s).
  3. Containers confirms oct-ssh-worker's restart count jumped and it isn't consuming the queue.
  4. You restart the worker on the host (docker compose restart oct-ssh-worker); its heartbeat goes green within 30s and the queued job runs to done.
Expected outcome: the Services page turned "a job is stuck" into "the ssh-worker died" in three reads — Heartbeats named the dead service, Containers showed why, and Active Work confirmed the queue drained after the restart.

Software Updates

Overview

Octotor checks once a day whether a newer release has been published, and shows the result in Settings ▸ Updates. When an update exists, users with Settings access also see a dismissible banner under the navigation bar. The check is anonymous: Octotor fetches one static file (the published version list) and sends nothing about this installation — no identifiers, no counts, no parameters. Installing an update stays a deliberate command-line step; nothing installs itself.

The Updates panel

Open Settings ▸ Updates. The Status panel shows:

#FieldMeaning
1Current versionThe version this installation is running
2Latest knownThe newest published version for your major release line, as of the last check
3Last checkWhen the last check ran and whether it succeeded — a failed check (for example, a site firewall blocking the deployment host) is recorded here and is harmless

Check now runs a check immediately. Daily update check is the opt-out: set it to Disabled and save to stop the background check — the Check now button keeps working either way.

Worked example: checking for and installing an update

  1. Open Settings ▸ Updates and click Check now.
  2. Suppose the panel now shows Current version v3.2.0 and Latest known v3.2.1, with the note "An update is available".
  3. On the host machine (SSH, not this web UI), run the upgrade one-liner:
    curl -fsSL https://get.octotor.com/upgrade | sudo bash
    It downloads the current release bundle, verifies the image signatures, backs up the database, and upgrades in place. To install a specific version instead of the latest:
    curl -fsSL https://get.octotor.com/upgrade | sudo bash -s -- --version=v3.2.1
  4. After the upgrade completes, reload this UI — the version in the corner and in Settings ▸ Updates now reads v3.2.1, and the update banner disappears on the next check.

If the panel shows "You are on the latest published version for this major", there is nothing to install. A separate note about a newer major version means a bigger upgrade exists — contact licensing to discuss it; the daily check never installs or changes anything by itself.

Support Bundle

Overview

When you contact support about a problem, they usually need to see the system's state: versions, service health, recent failures. The support bundle collects that into one downloadable file. Nothing is ever sent anywhere automatically — there is no upload feature by design. The file downloads to your computer, and you send it to support yourself by email or your own file share.

What is and is not in the bundle

#IncludedNever included
1Version, migration level, service heartbeatsCredentials or credential data
2License state summary (tier, expiry)Encryption keys, JWT secrets, API tokens
3Recent failed jobs (metadata only) + alert counts.env contents
4Error entries from the action logJob outputs and commands (can embed secrets)
5Settings, with secret-looking values maskedLicense key material

Worked example: sending a bundle to support

  1. Support asks: "please send a diagnostic bundle."
  2. Open Settings ▸ Support and click Generate bundle.
  3. Your browser downloads a file such as octotor-support-octotor-2026-07-28.tgz, and the panel confirms "Bundle download started".
  4. Attach that file to your reply to support (email or your file share). That's the whole flow — Octotor itself has sent nothing.

Host-side diagnostics (when the web UI can't help)

Container-level problems (a service that won't start, docker issues) aren't visible from inside the app. For those, run the host script on the server over SSH:

sudo bash /opt/octopus/scripts/octotor-diag.sh

It writes /tmp/octotor-diag-<date>.tgz with container status, redacted log tails, the compose configuration with all environment values stripped, disk/memory info, and network reachability checks. Send that file the same way — by your own hand.

Database Maintenance

Overview

The Maintenance section in Settings provides database lifecycle management: retention policies, optimization, and visibility into database health. All maintenance actions are logged and visible in the Investigate page.

Data Retention

Configure how long each type of data is kept before automatic cleanup:

SettingDefaultWhat It Deletes
Event Log Retention90 daysAction log entries (login, config changes, identity alerts, all system operations)
Job History Retention90 daysCompleted/failed jobs and their output text. Running and queued jobs are never deleted.
Identity History Retention30 daysDNS/SSH/collector verification records from the Identity tab
AI Conversations Retention90 daysGoose chat history and recipe execution logs
Attribute History Retention180 daysAttribute change log ("RAM changed from 8GB to 16GB")
Retention is applied by the scheduled maintenance service or when you manually prune data. Setting a retention value alone does not delete data — you must enable scheduled maintenance or click the manual delete button.

Scheduled Maintenance

When enabled, maintenance runs automatically on a cron schedule. Each run:

  1. Prunes data older than the retention limits
  2. Runs VACUUM ANALYZE (reclaims dead tuples, updates query planner)
  3. Deletes old backup files beyond the backup retention period

Cron examples:

ExpressionMeaning
0 2 * * *Daily at 2:00 AM
0 3 * * 0Every Sunday at 3:00 AM
0 */6 * * *Every 6 hours
30 1 1 * *1st of every month at 1:30 AM

Manual Actions

ActionWhat It DoesRisk
Backup StatusShows the most recent backup file, how long ago it was created, and a health indicator: green (✓ < 24h), yellow (⚠ 24–48h overdue), red (✗ > 48h — check host cron). Backups run via host cron (daily at 2 AM) using backup.sh.None (read-only)
Optimize (VACUUM ANALYZE)Reclaims space from deleted rows and updates query planner statistics. No locks, no downtime.None
Deep Optimize (VACUUM FULL)Rewrites all tables to reclaim maximum disk space. Locks all tables during operation — users may see errors.Brief downtime
Manual PruneDelete rows older than N days from a selected table. Shows a preview count before deleting.Permanent — deleted data cannot be recovered

Backup Architecture

Backups run via a host cron job (installed by setup-prod.sh). The cron entry runs backup.sh daily at 2 AM, which calls docker compose exec oct-postgres pg_dump — the postgres container's native pg_dump is always version-matched to the database. The last 7 backups are kept; older files are automatically removed.

Backup files are stored in data/backups/, which is mounted into the oct-api container as /data/backups. This allows the Settings > Maintenance page to list, download, and delete backup files. The Backup Status card shows freshness (how long since the last backup) with a color-coded health indicator.

To run a backup manually: cd /opt/octopus && ./backup.sh

To change the backup schedule: crontab -e on the host and edit the backup.sh cron entry.

Backup Security

Backup files contain the full database including:

The MASTER_KEY itself is not in the backup (it's in the .env file). Backup files should be stored securely and not shared.

Restore from Backup

A backup is a full logical pg_dump of the database — restoring it brings back every record: assets, attributes, users and permissions, settings, scheduler tasks, job history, alerts, and credentials. Two things live outside the dump and must be handled, or the restore is incomplete:

Verify a backup is good (non-destructive). From the stack directory: ./restore.sh data/backups/<file>.sql.gz. It restores into a throwaway *_verify database, compares every table's row count against the live DB, then drops it — the live data is never touched. A clean run ends with ✓ backup restores cleanly.

Actual restore (onto a fresh install or after data loss):

  1. Stand the stack up, but put the original MASTER_KEY / SNMP_CONNECTOR_KEY / JWT_SECRET / POSTGRES_PASSWORD in .env before first boot.
  2. Load the dump into the database:
    cd /opt/octopus
    ./backup.sh pre-restore          # safety snapshot of current state
    gunzip -c data/backups/<file>.sql.gz \
      | docker compose exec -T oct-postgres psql -U octopus -d octopus
    docker compose restart oct-api
    Or use the guarded helper: ./restore.sh data/backups/<file>.sql.gz --into-live (asks you to type RESTORE to confirm).
  3. Reissue the license for the new install (unless you restored the original /opt/octopus/license/).
Net: with a backup and the original .env, a full recovery re-configures nothing beyond the license (and only on new hardware). With the dump alone (no .env), you additionally re-enter credentials. The full runbook is docs/restore-procedure.md.

Database State Table

Shows per-table statistics: row count, disk size, dead tuples (indicates fragmentation), and when PostgreSQL last auto-vacuumed. Use "Get Info" to refresh individual tables or "Refresh All" for the complete view.

Octotor — IT Asset Management & Remote Operations Platform