Companies: one deployment, several
Administration
What a company owns of its own, and what every company shares.
A company hosting Nyxus can run one deployment for several companies: a managed service provider for its clients, or a group for its subsidiaries. Each company is kept apart. It has its own devices, findings, decisions, exceptions, rules, teams, identity map, registry additions, budget, history, campaigns, Jira connection, accounts, single sign-on and email.
What the companies share is the deployment itself: the receiver URL, the log store, the browser extension and the licence. The hosting company can also share its Grafana and Alertmanager with the companies it runs, and set a baseline of decisions and rules that applies to all of them.
A deployment starts with one company, the one hosting it. Nothing on this page shows, and nothing about how the deployment works changes, until a second company is added.
Adding a company
Settings > Companies, as an owner of the hosting company. The licence says how many active companies it covers: the activation key's companies, or one without a key. An archived company does not count.
Before you add the second company, move every device onto an enrollment token. While there is one company the receiver accepts the shared token (AUTH_TOKEN) from any device. Once there are two it refuses it, because a report made with the shared token cannot say which company it belongs to, and guessing would put one company's findings in another's estate. Settings > Companies says when a device last reported with the shared token.
A device reports for the company of the enrollment token it enrolled with, whatever its report says. To enroll a company's devices, switch to that company and download its collectors, or mint a token in Operations > Enrollment tokens. The scanner and discovery enroll with a token too, so each runs for one company. To move a device to another company, revoke it and enroll it again with the other company's token.
Working in a company
With two or more companies you can open, a switcher appears under the organisation name in the sidebar. Every page shows the company you have selected, your role changes with it, and every change you make applies to that company only, unless it is to something every company shares.
An owner of the hosting company also gets Overview > All companies: personal accounts still open, tools nobody has decided on, broken approvals, overdue findings and collector coverage for each company, read over your reporting window. Choose a company to work in it.
Who can open a company
Access works like an Azure subscription or an AWS member account: one list of accounts for the deployment, and a role in each company, given on purpose. Nothing reaches a company by default.
An account belongs to one company, the one it was made in, and has a role there: owner, admin, viewer or team viewer. It can open another company in two ways:
Every company. An account of the hosting company can be given its role in every company, including companies added later. This is for your own staff.
Access to a company. Any account can be given a role in another company, optionally until a date: an admin in Cerulean until the end of the month, say. When the date passes, the access stops.
Where an account has more than one role in a company, the higher one applies. The Primary owner is an owner in every company.
Who | Can |
|---|---|
Primary owner | Everything, in every company. Exactly one: the account the deployment was set up with. It cannot be deleted or demoted, only handed on to another owner of the hosting company, and it is the one account whose password still works where single sign-on is required |
Owner of the hosting company | Add, archive and delete companies; the licence and upgrades; what every company shares; give and take away access to any company |
Owner of a company | That company's settings, single sign-on and email; its own accounts; take away access someone was given to it |
Admin | Record decisions and change settings in that company. Shared settings need an admin or owner of the hosting company |
Viewer | Read that company |
Team viewer | Read that company's teams they are in. Company access only, because teams belong to one company |
The rules that stop a role in one company becoming a way into others:
Nobody gives a role higher than their own.
An account that reaches beyond one company (an account of the hosting company, the Primary owner, or anyone given access elsewhere) is managed only by the hosting company. A company's admins cannot reset its password, change its email or delete it.
Changing a password, email or role is judged by your role in the company the account belongs to, not in the company you are looking at. An owner of Cerulean by access is not an owner of anything else.
Shared settings, companies, the licence and upgrades need a role in the hosting company itself.
A company you have no access to is not found, rather than forbidden.
Settings > Account lists everyone who can open the company you are in, their role there and why they have it: Own company, Every company, Access until a date, or Primary owner. An owner of the hosting company opens Access on any account to see every company it reaches, and to give or take away access. Access review downloads that list as CSV. Every company downloads it for all companies at once, for an auditor or a client.
Every change of access is in the audit trail.
Signing in
Everyone signs in on the same page, with a username and password, or with Sign in with Microsoft. That asks for the person's work email first, finds their company's single sign-on from the corporate domains each company has set, and hands the address to Microsoft so it is not asked for twice. The page lists no companies, and an address no company claims gets the same answer as a typo.
Each company also has its own sign-in link, shown on Settings > Companies:
Each company sets up its own single sign-on, with its own Entra app registration, and its own email, in its own Settings. An account signs in through the single sign-on of the company it belongs to: your staff through yours, a client's people through theirs.
A client's people need to reach the portal. If it is only reachable over a VPN, giving them that access is the hosting company's decision. Nyxus does not change how the portal is exposed.
Requiring single sign-on
Each company can require single sign-on. It then applies to everyone who works in that company, the hosting company's staff included: a session signed in with a password is refused there, and a session signed in with single sign-on is not. An account whose companies all require it cannot sign in with a password at all. The Primary owner is the exception, and each such sign-in is in the audit trail.
Turning it on needs single sign-on on in that company, an owner who has signed in with it, and a single sign-on session of your own, so you do not end your own access by requiring it.
What every company shares
Shared by every company | Each company's own |
|---|---|
Receiver URL and portal URL | Devices, enrollment tokens, findings |
Log store and its credentials | Decisions, exceptions, rules, teams |
Browser extension ids and update URLs | Identity map and registry additions |
The receiver's metrics endpoint, each company's series labelled | Budget, history, campaigns, Jira |
The licence | Accounts, single sign-on, email, webhook, digest webhook, corporate domains |
Grafana and Alertmanager, unless the hosting company shares its own | |
Requiring single sign-on, and how recently a sign-in must have happened |
With more than one company, the settings pages say which settings are shared and mark each shared field every company. A company's own accounts cannot see or change them.
The identity map file (IDENTITY_MAP, or portal.identityMap in the chart) is the hosting company's. Every other company attributes devices only from the identity map saved in the portal for that company, so a local username in the file never names a person in another company.
Email, alerts and sign-in
Email. Each company sends through the relay saved in its own Settings: invites, reminders, escalations and campaigns. The relay in the receiver's environment (
SMTP_HOSTand the rest) is the hosting company's. A company with no relay of its own sends nothing and says so, rather than mailing its people from the hosting company's address.Links in email carry the company. An invite opens that company's sign-in page, and a reminder opens the finding in that company, including for the hosting company's staff.
Webhook. Each company's own.
Grafana and Alertmanager. Each company can save its own, and its own always wins. The ones in the environment (
GRAFANA_URL,GRAFANA_PANELSandGRAFANA_DASHBOARD_UIDon the portal,ALERTMANAGER_URLon the receiver) are the hosting company's. A client company with none of its own uses the hosting company's only when the hosting company's staff share it with that company, Grafana and Alertmanager each separately, in that company's settings. Otherwise it has no panels and pages nowhere, rather than showing panels over other companies' findings or paging into their routing. Every alert carries acompanylabel, so one shared Alertmanager can route companies apart.Single sign-on is set up per company, and each company can require it. See Requiring single sign-on.
The weekly digest is sent for each company, covering its own findings, to the digest webhook saved in its settings.
DIGEST_WEBHOOK_URLin the portal's environment is the hosting company's. A portal withoutRECEIVER_PORTAL_TOKENcannot read the companies' settings, and sends only that one.Metrics. The receiver's findings, active personal-account sessions, devices reporting, last scan time and registry size carry a
companylabel. The log-store push and tool-normalisation counters are the deployment's and have none.
A baseline of decisions and rules
The hosting company can record a decision for every company on a tool's AI register entry. It applies in each company that has not recorded its own decision on that tool. A company's own decision always wins, and if the company removes it, the shared decision applies again. A shared approval cannot name teams, because each company has its own.
A rule created with Apply to every company runs in every company's register. Any company can turn it off for itself (Turn off here). Only the hosting company can edit it, turn it off everywhere or delete it.
The register and Governance > Rules mark both Every company. A company's own accounts can read the baseline but cannot change it. See Governance.
A governance file mounted on the portal (GOVERNANCE_PATH) is read for every company as well, and a decision recorded in the portal wins over it, in each company, as it does with one company.
Archiving, exporting and deleting
Archive stops a company. Its devices are refused at /report and /enroll, its accounts can no longer use the portal, and it leaves the switcher. Its records are kept until a date you choose. After that date the receiver deletes it on its next remediation check (every REMEDIATION_CHECK_MINUTES), and keeps a deletion record. Restore undoes an archive before then, if the licence has room.
Export downloads a zip:
File | What it holds |
|---|---|
| the company's records as they stood: decisions, exceptions, rules, teams, identity map, history, budget, campaigns, settings. Stored credentials are removed (SMTP password, webhook URL, single sign-on client secret, Jira token, budget vendor keys), and the copy is vacuumed so nothing removed stays in it |
| the company's findings from the log store, one per line, over |
| the company, when the export was taken, how many findings were read and whether the cap was hit, and a SHA-256 checksum for each file |
The deployment holds accounts, devices and enrollment tokens for every company, so they are not in company.db.
Delete now works on an archived company, after you type its name. It removes the company's devices, enrollment tokens, accounts and their sessions, and its database file. It also asks the log store to delete the company's findings: POST /loki/api/v1/delete with the selector {company="<id>"}. Loki deletes in its compactor, later, and only where deletion is enabled (limits_config.deletion_mode and a compactor with retention). The deletion record says what the log store answered. A store that refused, or has deletion off, keeps the findings until its own retention removes them.
Deletion records stay on Settings > Companies after the company is gone: when it was deleted, by whom or by retention, why, and what was removed. You can download them as CSV.
How companies are kept apart
In the receiver. Every admin request runs in one company, named by the
X-Nyxus-Companyheader (the portal sets it from the switcher). A session works there only with a role there, from its own company, every company or access given to it, and only if the company's sign-in policy accepts how it signed in; the role is read again on every request. A company it cannot open is not found. The API credentials default to the hosting company.On disk. Each company's records are in their own SQLite file, in
companies/next toSTATE_DB_PATH. The main file keeps the hosting company's records, and the accounts, devices and enrollment tokens of every company. Back up the whole directory.In the log store. Each finding carries a
companylabel, and every read the portal makes selects on it. Findings from before companies existed belong to the hosting company.In the portal. Derived pages and caches are kept per company, and the portal's scheduled work (history, rule checks) runs once for each company.
API
Through the portal, with a session. Changes need an owner of the hosting company.
Method | Path | |
|---|---|---|
GET |
| the companies this account can work in, the licence allowance, and when the shared token last reported |
POST |
| work in a company ( |
POST |
| add one ( |
POST |
| rename ( |
POST |
| archive, keeping its records until |
POST |
| restore an archived company |
GET |
| the export zip |
POST |
| delete an archived company now ( |
GET |
| deletion records; |
GET |
| every company at a glance. |
GET |
| everyone who can open this company, their role here and why. |
GET |
| the access review for every company you can open |
GET |
| every company one account reaches, and why |
POST |
| give a role in a company ( |
POST |
| take it away |
POST |
| an account of the hosting company reaches every company, or stops ( |
POST |
| hand the Primary owner role on ( |
GET |
| which company's single sign-on a work address uses. |
The receiver's own routes are described in How Nyxus is put together.
Limits worth knowing
One log store, not one per company. Findings are separated by a label, not by a tenant. Anyone who can query the log store directly, including through Grafana's Explore, can read every company's findings. Give Grafana and log-store access to the hosting company's staff only.
A shared Grafana is not a boundary. The dashboard has a company filter, and the portal opens its panels on the company being worked in, but anyone who can reach that Grafana can change the filter. Share it only with companies that may see each other's findings, such as a group's own subsidiaries, and give a client that must not its own Grafana.
The hosting company's owners can open everything. That is intended. A client that must not be visible to its provider needs its own deployment.
Access ends on a date, not a time. Access given until 30 September works through that day, in UTC.