Platform Modules
Asset Inventory Management (Fleet) — Docs
Discover, enroll, and continuously monitor workstations, servers, and cloud instances with osquery — powered by the Unicis Fleet backend and wired into your compliance evidence.

The Asset Inventory Management module gives your team a live inventory of every endpoint it operates — laptops, workstations, servers, VPS, and cloud instances — collected directly from the machines themselves rather than from a spreadsheet somebody last updated a year ago.
It is built on osquery, the open-source agent that exposes an operating system as a relational database, and on Unicis Fleet, our multi-tenant osquery TLS management server. Each team gets its own isolated Fleet tenant, its own enrollment secret, and its own TLS material.
Asset inventory is a hard requirement of practically every framework Unicis supports — ISO/IEC 27001:2022 (A.5.9 Inventory of information and other associated assets), NIS2, CIS Controls v8.1 (Control 1 & 2), SOC 2, and NIST CSF 2.0 (ID.AM). This module turns that requirement from a manual exercise into a continuously updating, queryable data source.
Architecture
┌──────────────────┐ ┌────────────────────┐ ┌──────────────────┐
│ Unicis Platform │ REST │ Unicis Fleet │ TLS │ osquery agents │
│ (Next.js) │◄──────►│ (Flask API) │◄──────►│ workstations, │
│ Asset module UI │ │ multi-tenant │ │ servers, cloud │
└──────────────────┘ └────────────────────┘ └──────────────────┘- Unicis Platform renders the module, holds team membership, RBAC, subscription state, and the enrollment/connection records.
- Unicis Fleet is the osquery TLS server. It terminates the agent protocol (
/api/enrollment,/api/config,/api/distributed/read,/api/distributed/write,/api/logger) and exposes a management API under/api/v1/manager/<team_id>/…. - osquery agents run on your endpoints, enroll with the team’s enrollment secret, pull their configuration, execute scheduled and distributed queries, and ship results back over TLS.
Every management call is scoped by team_id, so one team can never read another team’s nodes, queries, packs, or results.
Access model
Subscription gate
The module is only rendered for teams on an active Ultimate subscription. Without a plan, the Asset Management pages resolve to nothing.
Role-based access control
Asset Management adds six RBAC resources on top of the platform’s existing model:
| Resource | Controls |
|---|---|
asset_dashboard | The asset overview, platform breakdown, and task analysis |
team_fleet_connect | Connecting, bootstrapping, and disconnecting the Fleet account |
team_fleet_node | Assets (nodes) — read, tag, export, delete |
team_fleet_query | Saved queries and distributed (live) queries |
team_fleet_pack | Query packs |
team_fleet_tag | Asset tags |
Owner and Admin hold all actions on all six. Member holds a reduced set. Auditor is read-only on asset_dashboard and team_fleet_node — auditors see the inventory and the analysis, but never the node table controls, the enrollment secret, or the agent installation commands.
Fleet account
Platform authentication is not Fleet authentication. Each user holds a separate Fleet account, and connecting it issues a short-lived Fleet access token stored in a SameSite=Strict cookie. Until that token exists, every Asset Management page shows the Fleet Connection Is Required gate.
Getting started
1. Bootstrap the team’s Fleet account (admins)
A team Owner or Admin opens Asset Management → Asset and selects Order a Secret. Set a Fleet password (minimum length is enforced by the platform password policy), and the platform calls POST /api/fleet/bootstrap, which:
- creates the Fleet account and the Fleet team for your
teamId, - generates the team’s enrollment secret,
- returns a Fleet token that is stored in the Fleet access cookie.
2. Enroll teammates
From the same screen, an admin can send a Fleet invitation to any platform team member. POST /api/fleet/enroll:
- creates a temporary Fleet account for that user via the Fleet service token,
- adds them to the Fleet team with a role mapped from their platform role (
owner→owner,admin→admin, everything else →member), - writes a
FleetEnrollmentrecord with a single-use token that is valid for 24 hours, - emails the user an enrollment link plus a generated temporary password.
The invitee follows the link, enters the temporary password, and is immediately required to set a permanent one. Re-inviting a user whose invitation is still pending returns the existing expiry rather than issuing a second token; re-inviting an already-enrolled user is rejected.
3. Connect
Any enrolled user selects Connect Fleet, enters their Fleet password, and the platform exchanges it for an access token. Forgot the password? The Forgot Fleet Password flow sends a reset link that expires in 60 minutes.
4. Install the agent
Open the asset dashboard and select Add Asset. The dialog generates ready-to-paste installation and enrollment commands for the platform you pick:
| Tab | What you get |
|---|---|
| Windows | PowerShell download of the osquery .msi plus the osqueryd service flags |
| Linux (deb) | .deb download and install, plus the flagfile |
| Linux (rpm) | .rpm download and install, plus the flagfile |
| macOS | curl download of the pinned osquery .pkg, sudo installer install, and the osqueryd invocation |
| Advanced | The raw flag set, for configuration-managed fleets (Ansible, Puppet, Intune, MDM) |
The generated flags pin the agent to your tenant:
--host_identifier=uuid
--config_plugin=tls
--config_tls_endpoint=/api/config
--config_tls_refresh=10
--enroll_tls_endpoint=/api/enrollment
--enroll_secret_path=<path to your team secret>
--distributed_plugin=tls
--distributed_interval=10
--distributed_tls_read_endpoint=/api/distributed/read
--distributed_tls_write_endpoint=/api/distributed/write
--logger_plugin=tls
--logger_tls_endpoint=/api/logger
--logger_tls_period=5
--tls_hostname=<your Fleet host>
--pack_delimiter=/
--utc
--enroll_alwaysWhen the deployment issues per-team TLS material, the dialog also shows the Team TLS Certificate and adds --tls_server_certs=./ca-cert.pem (.\ca-cert.pem on Windows). Save the CA content to that file before starting the agent.
The default agent version shipped in the commands is osquery 5.23.1, overridable per deployment with OSQUERY_AGENT_VERSION.
On macOS the commands install the pinned release package rather than the latest Homebrew cask, so every Mac in the team runs the version the platform generated the commands for:
curl -fsSL "https://github.com/osquery/osquery/releases/download/<version>/osquery-<version>.pkg" -o /tmp/osquery-<version>.pkg
sudo installer -pkg /tmp/osquery-<version>.pkg -target /
sudo /opt/osquery/lib/osquery.app/Contents/MacOS/osqueryd --versionThe macOS enrollment command therefore invokes osqueryd by its full path, /opt/osquery/lib/osquery.app/Contents/MacOS/osqueryd, because the package does not put the daemon on PATH.
The asset dashboard
Once at least one agent has enrolled, the dashboard shows:
- Total Assets and a per-platform breakdown (Windows / Linux / macOS) as a pie chart
- Asset Task Analysis — how enrolled assets relate to compliance tasks in the platform
- The asset table — searchable by name or asset ID, filterable by
active/inactive, and paginated
Selecting an asset opens its detail page with four tabs:
| Tab | Contents |
|---|---|
| Overview | Identity, platform, enrollment time, last check-in, tags |
| Status Logs | The agent’s own status/health log stream |
| Result Logs | Query results shipped by the agent |
| Asset Configurations | The exact configuration this node received — host identifier, logger plugin, logger endpoint, logger TLS period, pack queries, scheduled queries, plus a raw config view |
Assets can be tagged, exported, and (for roles that hold delete) removed along with their logs and results.
Tags
Tags organise the inventory and target configuration. A tag can be attached to assets and to packs, so a pack scheduled against production-linux reaches exactly the nodes carrying that tag. Tags are managed under Asset Management → Tags, and each tag lists its associated analysis.
Queries
Asset Management → Queries is your library of saved osquery SQL. A query carries a name, description, the SQL body, a platform target, and a schedule interval.
The interval is chosen from a fixed set rather than typed as a number of seconds:
| Option | Seconds |
|---|---|
| 1 hour | 3600 |
| 6 hours | 21600 |
| 12 hours | 43200 |
| 24 hours (default) | 86400 |
| 1 week | 604800 |
| 1 month | 2592000 |
A query created before these options existed keeps its own interval — the edit form shows it as Current: <n> seconds alongside the presets, so editing another field never silently reschedules the query.
The lower-level osquery config fields — version, shard, and value — are no longer part of the form. They are submitted with platform defaults (1) so the create and edit dialogs stay focused on what actually differs between queries.
SQL validation
The SQL body is parsed and validated in the browser before the query is saved, in the query, pack, and distributor forms alike. A query is rejected when it is:
- empty — SQL query is required
- not parseable as SQLite SQL — Invalid SQL syntax
- more than one statement — Only one SQL statement is allowed
- anything other than a
SELECT— Only SELECT queries are allowed
The form focuses the offending field and shows the message inline. This is a guardrail, not a security boundary: osquery’s own virtual tables are read-only by design, but rejecting non-SELECT statements early avoids scheduling a query the agents would only refuse later.
Typical compliance queries: is FileVault/BitLocker enabled, is the firewall on, is automatic update enabled, which browser extensions are installed, which processes listen on which ports, which users hold local admin.
Packs
A pack is a group of queries with a schedule, assigned to tags. Packs are how you turn one-off checks into continuous monitoring: define the queries once, schedule them, attach the pack to the tags that describe the machines they apply to.
Creating or editing a pack asks only for a name and a description; platform, version, and shard are filled in with defaults. The pack’s real targeting comes from the tags you attach it to and the queries it contains.
Deleting a pack permanently deletes everything associated with it — the UI requires you to type DELETE to confirm.
Distributors (live queries)
Distributed queries answer questions now instead of on a schedule. Create a distributor, target it at assets, and the next time those agents call /api/distributed/read they pick the query up. Results land under the distributor and are readable by status:
new → pending → complete / failed
This is the tool you reach for during an incident: “which of our machines has this file”, “which machines are running this version”, “who still has that vulnerable package”.
A distributor needs both a valid SELECT statement and at least one target — assets picked directly, tags, or both. Submitting without a target is rejected with Select at least one asset.
Reading results
Result rows across queries, packs, and distributors identify the machine they came from rather than only its node ID. Each result shows the asset name, its owner (full name, or email when no name is set), and the host identifier, next to the row’s action badge:
| Action | Meaning |
|---|---|
added | The row appeared since the previous run |
removed | The row disappeared since the previous run |
failed | The query did not run successfully on that asset |
A failed row carries the error osquery reported, so a broken query is visible as a query problem instead of an empty result set. Results produced by a pack are labelled with the pack name, taken from osquery’s pack/<name>/<query> naming.
Disconnecting and data retention
A team Owner or Admin can disconnect Fleet from Asset Management → Asset. Disconnecting:
- sets the team’s
FleetConnectionstatus toDISCONNECTED, - records
disconnectedAtand computesdeleteAfterfromFLEET_DISCONNECT_RETENTION_DAYS, - expires the Fleet access cookies immediately,
- leaves the module visible but gated behind a notice showing the deletion date.
Reconnecting before that date restores access and clears the schedule. If the retention window elapses, a background worker (fleet-retention-cleanup) calls the Fleet service delete endpoint for the team, removes the team’s FleetEnrollment records, and marks the connection DELETED. Failures are recorded on the connection record (cleanupError, lastCleanupAttemptAt) and retried on the next run — the worker processes at most 25 teams per run.
Platform API endpoints
These are the platform-side routes backing the module (all under /api/fleet):
| Endpoint | Method | Purpose |
|---|---|---|
/api/fleet/bootstrap | POST | Create the team’s Fleet account and enrollment secret |
/api/fleet/connect | POST | Exchange a Fleet password for an access token |
/api/fleet/connection | GET, POST | Read connection status; disconnect / reconnect (team admin only) |
/api/fleet/enroll | POST | Invite a team member (temp password + 24h token) |
/api/fleet/enroll/verify | GET | Validate an enrollment token |
/api/fleet/enroll/complete | POST | Complete enrollment after first login |
/api/fleet/access/verify | GET | Verify the current Fleet access token |
/api/fleet/check-account | GET | Whether the current user has a Fleet account |
/api/fleet/secret | GET | Read the team enrollment secret |
/api/fleet/ensure-secret | POST | Create the enrollment secret if missing |
/api/fleet/sync-member | POST | Sync platform role → Fleet role |
/api/fleet/change-password | POST | Change the Fleet password |
/api/fleet/forgot-password | POST | Send a Fleet password reset link (60 min) |
/api/fleet/revoke | POST | Revoke a member’s Fleet access |
Fleet API endpoints
The Fleet backend exposes the osquery TLS endpoints and a management API. The osquery-facing routes are available both with and without the /api/v1 prefix, because osquery deployments differ:
| Route | Purpose |
|---|---|
POST /api/enrollment | Agent enrollment with the team secret |
POST /api/config | Agent configuration |
POST /api/distributed/read | Agent picks up distributed queries |
POST /api/distributed/write | Agent returns distributed results |
POST /api/logger | Agent ships scheduled query results and status logs |
Management routes are scoped per team:
| Route | Purpose |
|---|---|
GET /api/v1/manager/<team_id>/nodes[/<status>][/<page>][/<per_page>] | List assets |
GET /api/v1/manager/<team_id>/node/<node_id> | Asset detail |
GET /api/v1/manager/<team_id>/node/<node_id>/config | Configuration delivered to the asset |
GET /api/v1/manager/<team_id>/node/<node_id>/logs[/<page>] | Status logs |
POST /api/v1/manager/<team_id>/node/<node_id>/tags | Tag an asset |
DELETE /api/v1/manager/<team_id>/node/<node_id>/delete | Delete an asset |
GET/POST/PUT/DELETE /api/v1/manager/<team_id>/quer{ies,y}/… | Saved queries |
GET/POST/PUT/DELETE /api/v1/manager/<team_id>/pack{s,}/… | Packs |
GET/POST/PUT/DELETE /api/v1/manager/<team_id>/tag{s,}/… | Tags |
GET/POST/DELETE /api/v1/manager/<team_id>/queries/distributed/… | Distributed queries and results |
GET /api/v1/manager/<team_id>/analysis/query | Query result analysis |
GET /api/v1/manager/<team_id>/analysis/tasks | Task analysis |
GET/POST/DELETE /api/v1/fleet/teams/<team_id>/secret | Enrollment secret |
POST /api/v1/fleet/teams/<team_id>/secret/renew | Rotate the enrollment secret |
Interactive API documentation is served at /documentation with the OpenAPI definition at /api/v1/swagger.json.
Configuration
Self-hosted deployments need the following environment variables on the platform:
| Variable | Purpose |
|---|---|
FLEET_API_URL | Base URL of the Unicis Fleet API (server-side calls) |
FLEET_SERVICE_TOKEN | Service token for privileged Fleet operations (account creation, member sync, team deletion) |
NEXT_PUBLIC_FLEET_API_URL | Fleet API URL used by the browser |
NEXT_PUBLIC_FLEET_API_HOST | Fleet host used in generated agent commands (--tls_hostname) |
OSQUERY_AGENT_VERSION | osquery version used in generated install commands (default 5.23.1) |
FLEET_USE_TLS_SERVER_CERTS | Emit --tls_server_certs and show the team CA certificate |
FLEET_DISCONNECT_RETENTION_DAYS | Grace period before disconnected teams’ Fleet data is deleted (default 0) |
Getting the Fleet source
Unicis Fleet is not yet published in our public GitHub organisation. It is distributed through a private GitLab repository, and access is granted to Ultimate plan customers who run their own deployment — contact us to be added. Teams on Unicis Cloud never need it: Fleet is hosted and operated for them.
This is a licensing constraint rather than a change of direction. Asset Inventory Management is an Ultimate-plan capability and the platform does not have licence and billing management yet, so there is no mechanism today that would let us publish the source while keeping the plan meaningful. Once that ships — giving us proper visibility over entitlements and how they are managed — Fleet moves to a public repository alongside the rest of Unicis.
The Fleet backend itself is deployed with Docker Compose and scales horizontally:
docker-compose up -d --build --scale web=5Rotating the enrollment secret
Asset Management → Asset → Renew secret replaces the current enrollment secret with a new one. Delete secret removes it entirely; both actions ask for confirmation first, and once no secret exists the screen shows a Secret not found panel with Order a Secret instead of a masked value.
Deleting is idempotent: if the secret is already gone on the Fleet side, the platform reports success rather than an error, so a partially completed deletion can be retried safely.
Troubleshooting
“Fleet Connection Is Required” after entering the password. The access token is stored as a cookie; check that the Fleet API is reachable from the browser and that its CORS configuration allows your platform origin. A non-2xx response from the Fleet API’s account endpoints will surface in the browser as a CORS error, because error pages are returned without CORS headers.
“Secret not found. Please generate a new secret.” The team was never bootstrapped, or the secret was deleted. An admin re-runs Order a Secret, or the platform recreates it via POST /api/fleet/ensure-secret.
Agent enrolls but no results arrive. Confirm --logger_tls_endpoint=/api/logger is reachable, that the agent’s clock is correct (results are logged in UTC), and that at least one pack is scheduled against a tag the node carries.
Invitation expired. Enrollment tokens are valid for 24 hours. Ask an admin to resend the invite.
Related
- Cybersecurity Management System (CSC) — map asset evidence to controls
- Cybersecurity Risk Management (RM) — assign asset owners and treat asset-related risks
- Tasks — where asset-derived findings become tracked work
- Settings — team roles used by Fleet role mapping
Try this in the Unicis Platform
Community Edition is free and self-hosted. The Business Edition adds hosting, integrations and priority support.
Record of Processing Activities
Transfer Impact Assessment
Privacy Impact Assessment
Cybersecurity Controls
Cybersecurity Risk Management
Interactive Awareness Program