Organizations & Teams
An organization is noBGP's sharing and billing boundary. Networks, nodes, and services belong to an organization, its members can see them, and the organization receives one bill.
⚠ Seeing and using are two different things, from router 0.4.179. A member may run commands on a machine when it is shared with its organization — the default — or when they are named on its access list; machines set to private stay with the people who control them. See What you can have on a node.
Personal organizations
Every account gets a personal organization automatically at signup — you are its Owner, and everything you create lands there by default. If you only ever use noBGP solo, you never need to think about organizations at all.
A personal organization carries no name of its own until you give it one. What you see beside it is derived: your profile name, or — if you have not set one — the email address you sign in with. Set your profile name and the label follows it immediately, with nothing to rename; a social login that supplies a name fills an empty profile name in for you the next time you sign in that way.
Naming the organization itself is the other way to fix the label, and it is the one that sticks: rename it and that explicit name wins over the derivation from then on, whatever your profile name later says.
Team organizations
Create a separate, shared organization when you want a team to operate the same infrastructure:
Create an organization called "Acme Corp"
Your AI assistant uses the org_create tool; you become the new organization's Owner. You can also manage organizations in the web app under Account.
A new organization starts on the Free plan, from router 0.4.127 — with Free's allowances, Free's hard caps and Free's place in the three-free-organizations count. Through router 0.4.126 a team organization was created on no plan at all, which is not a neutral state: an organization with no plan was treated as the unmetered tier, so no allowance bounded it and it was invisible to the free-organization limit. Subscribing has always set the plan, so an organization that has been through checkout was never affected, and existing organizations with no plan have been moved to Free.
New networks go to your personal org unless you target the team org (org_id on network_create, or select the organization in the app). Nodes are billed to the organization that owns the network they registered in.
How many free organizations you may own
An account may own at most three organizations on the Free plan, from router 0.4.103, and the personal organization you were given at signup is one of the three. A fourth is refused with a limit error naming the two ways forward — upgrade one of them to a paid plan, or have someone else own the next one.
Only ownership of free organizations counts. Being invited into someone else's organization never does, however many you join, and paid and Enterprise organizations are not counted at all. Transferring ownership of a free organization moves it onto the recipient's three, so a transfer that would take them past the limit is refused as well.
The reason is the allowances: each free organization carries a full one of its own — 50 GB of bandwidth, $1 of provisioned compute, 10 GB of storage — see Plans & Billing.
Which network a call lands in
Once you belong to more than one organization, "my network" stops being obvious — and the tools resolve it so that a shared organization's network is something you name rather than something you land in:
- Omitting the network name means your sole network, judged inside your personal organization. One personal network wins even when the team orgs you belong to hold a dozen more. Only an ambiguous personal side — two personal networks, or none at all alongside several shared ones — gets you the
invalid_argsasking which network you meant. - A name that exists in two of your organizations resolves to the personal one first. Network names are unique per creator rather than globally, so you and a teammate can each have a
home. - An id means exactly that network. Passing a network id reaches the network it identifies, in whichever organization that is — no name lookup happens, so it can't be rebound to a personal network sharing the name.
Nothing changes for a solo account, or for anyone whose networks all live in one organization: a single network is still picked automatically. The MCP Reference lists which parameters this covers.
Roles
Each member has one role:
| Role | What it is |
|---|---|
| Owner | Everything, including billing, SSO and transferring ownership. Only an Owner reads money data: invoices, the card, the credit balance and the spend cap |
| Admin | Manages members and invites, reads the audit log, and controls every network and node in the organization. An Admin cannot manage Owners and does not read money data. An Admin sees which plan and interval the organization bought |
| Member | Sees the organization's networks and nodes, creates networks and nodes, and controls what they own. Uses other people's nodes only as far as those nodes' controllers allow |
A role does not decide everything about a node. A node's controllers are its owner and the organization's Owners and Admins, and they decide who else may use it and who may run as root on it. Roles & Permissions has the full rules and the who-may-do-what table.
whoami reports your role in each organization, and network_directory reports what you hold on each node in my_access: control, root, use or none.
An organization always keeps at least one Owner. The Owner transfers ownership to another member and becomes an Admin.
What happens to a leaver's infrastructure
When a member leaves or is removed, their networks and nodes move to an Owner of the organization. Nothing is deleted, and the result lists what moved. See When a member leaves.
noBGP staff access
The setting "Allow noBGP support" decides whether noBGP staff may act in your organization. It is on by default. An Owner or Admin can turn it off, and give staff temporary access for a number of hours. Every staff action is written to the audit log. See noBGP support for what staff may do.
Inviting members
Invite teammates by email from Account → Members in the web app. Each invite:
- Emails a one-time link that expires after 7 days
- Grants a role you choose on acceptance (
memberby default) - Can be resent — resending rotates the link, so the previously issued one stops working
Admins may invite Members or Admins; only an Owner can bring in another Owner, and only an Owner may revoke or resend an Owner's invite.
The link is not a bearer credential. Accepting it requires the caller's verified sign-in address to match the address the invite was sent to — so forwarding the mail to a colleague does not let them join, and an unverified address cannot accept at all. To invite someone else, send a new invite. A mismatch is refused with forbidden.
Renaming an organization
Organizations are renamed by an Owner or Admin — inline on the Members page in the app, or through the org_update tool.
A personal organization can be renamed too, from router 0.4.78, and you are always its Owner so nothing gates you. The name you set replaces the derived label permanently: editing your profile name afterwards no longer moves it. Earlier routers refused the call and the label could only be changed by editing your profile.
Audit log
Actions against the organization's infrastructure are recorded in a per-organization audit log, visible to Owners and Admins in the app under Account → Audit (filterable and paginated).
⚠ Money figures are withheld from an Admin, from router 0.4.179. An Admin reads every row; the amounts are stripped from the rows that carry them, because money data is the Owner's. Which plan and interval the organization bought is not money data and stays visible.
Each entry captures who called which tool, the target it acted on, when, and the outcome — including the failures: a call that was denied or that errored is recorded with the failure and its error code, which is what makes who tried and was refused answerable at all. Tool arguments are not recorded — they can contain secrets (tokens, keys, sensitive paths), so only that narrow summary is kept. In particular the router stores no command text anywhere: not in a log line, not in a table, not in an audit row.
What is recorded
From router 0.4.98 the log covers calls that address a node, not only calls that change something.
| Area | Tools |
|---|---|
| Networks | network_create, network_update, network_delete — the last two from router 0.4.179 |
| Enrollment keys — router 0.4.179+ | network_key_list, network_key_create, network_key_update, network_key_delete, and network.key_read for the config code that hands a key to a machine. The list shows no secret, and who looked at a network's credentials is still worth a row |
| Nodes | register_node, provision_node, deprovision_node, node_rename (router 0.4.120+), task_deadline_set (router 0.4.125+), task_stop (router 0.4.149+), and from router 0.4.179 node_delete, node_transfer (with the old and the new owner) and node_channel_set |
| Who may use a node — router 0.4.179+ | node_access_list, node_access_set, node_access_grant, node_access_revoke, node_access_request, node_access_decide. Every change to who may use a node, or hold root on it, is a row — and so is the read, because it names who holds root |
| Work on a node — router 0.4.98+ | command, file, the per-op fs_read / fs_write / fs_list / fs_stat / fs_delete / fs_mkdir / fs_edit tools, fs_copy, fs_grep, fs_glob |
| Node diagnostics and settings — router 0.4.98+ | node_logs, node_config_get, node_config_set, net_peers, net_interfaces, net_metrics, net_routes, net_dns, and net_reach / net_reach_many — router 0.4.155+ |
| Subscriptions — router 0.4.98+ | command_subscribe, fs_subscribe, fs_grep_subscribe, presence_subscribe |
| Services | service_publish, service_update, service_delete |
| Organization | org_create, the org_invite_* and org_member_* tools, org_leave, org_transfer_ownership, org_sso_setup, org_sso_set_enforced, and from router 0.4.179 org_support_access — one action per operation (org.support_read, org.support_setting, org.support_grant, org.support_end) |
| Billing | create_checkout, create_billing_portal, set_spend_cap, billing — router 0.4.114+ — and credit — router 0.4.118+ |
| Feedback | feedback_submit — that a report exists and who filed it, never a byte of its text |
Three kinds of call were reaching no row at all, and are recorded from router 0.4.176. Each of them named no machine, and the log is filed against the organization that owns the target — so with nothing resolved there was nothing to file the entry against, and it was dropped:
- A call that continues a session.
commandandfilereturn an id you call back with to read output, send input or move the next chunk, and only the call that opened the session named a node. The opening call decides which organization the session belongs to, and every later call on that id is filed there. - A call against a network's shared drive — addressed by network alone, with no node in it. noBGP serves those bytes itself, so nothing resolved a machine. A write to or a delete from a drive every member of the network reads is now filed against that network's organization.
- A call against a machine's storage area (
storage: true), likewise served by noBGP. Filed against the organization that owns the machine, since the area follows the machine rather than a network.
A continuation answered by a different instance than the one holding the session is recorded too, from router 0.4.177. That instance forwards the call and holds no machine for it, so through router 0.4.176 it was the one continuation still dropped; the instance that holds the session now hands it the attribution along with the answer, so the entry lands in the same organization as the opening call. The record it passes names the session's owner, so a caller who is not the owner never files a row in the owner's organization.
A failed call made over MCP now records why it failed, from router 0.4.178. The entry always said whether a call succeeded; on the MCP endpoint the error code and message beside it were blank, because a tool failure arrives there on the result rather than as a transport error and the recorder read only the latter. So who was refused, and what refused them was answerable for a call made over the REST endpoints and not for the same call made by an AI client. Both now record the same code and message. A call whose arguments the endpoint itself rejected is recorded as invalid_args, which is what it is — the caller sent arguments the tool does not accept.
A usage credit call names which operation it ran, from router 0.4.118 — billing.credit_status, billing.credit_buy, billing.credit_auto_reload, billing.credit_spending or, from router 0.4.127, billing.credit_purchases — rather than one entry for all of them, so reading a balance and charging a card are never the same row. The operation is recorded before it runs, which is what puts an attempted and refused charge on the record too. A plain billing.credit entry means the call never reached the operation at all — refused by the Owner-only gate, or naming an operation the tool does not serve. Through router 0.4.117 these calls were recorded nowhere.
Some entries have no actor, because noBGP wrote them rather than a person. Two of them arrived in router 0.4.127 — billing.credit_auto_reload_cleared and billing.spend_cap_cleared, written when cancelling a subscription erases those settings. They carry the erased numbers in metadata, which is what makes the log a record of the setting rather than a notice that it is gone — read them back and enter them again if you re-subscribe. The third is the machine stop below.
When noBGP stops a machine, the log says so
From router 0.4.155 a machine noBGP stops writes its own node.stop entry, with no actor — because nobody called anything — and a reason saying which line was reached. Before it, a machine that hit its deadline or ran out of compute simply became an offline node with nothing in the log: why did this stop was answerable from the machine's own absence and from nothing else.
reason | What happened |
|---|---|
deadline | The machine reached its deadline |
allowance | The organization reached its included compute |
credit_empty | The credit balance is empty |
lapsed | A payment failed and the subscription lapsed |
provider | The container had already stopped on its own — noBGP asked, and found nothing left to stop |
user_stop | A person called task_stop; this row has an actor |
A node.deprovision entry carries reason too — user or provider. reason is absent when the call closed no task, because the machine had already stopped or the call failed.
Finding an entry
The app's audit page passes these through to org_audit_list, so the same filters are available over REST:
- Free-text search (
q), from router 0.4.155: a case-insensitive substring matched against the action, the actor's email, the target id and the target's name. Searching for a node by the name you know it as is what this adds — the log is keyed on ids, so before it the name was not searchable at all. From router 0.4.179 it also matches the actor's name, so looking a colleague up by the name you know them by works; before that only their email address matched. - Action filter (
action) — also a substring, sodeprovisionmatchesnode.deprovision.%and_are matched literally in both, not as wildcards. - Actor filter (
actor_id), andlimit/offsetpaging — 50 rows by default, 200 at most. - The list of actions this organization has actually used (
include_actions), from router 0.4.155, so the action filter can be offered as a menu rather than typed blind.
An entry names its target as well as identifying it, from router 0.4.155: target_name is resolved when the log is read, so a renamed node shows its current name on all of its old rows, and a node, network or service that has been deleted still shows the name it had, with target_deleted: true beside it. target_id remains the key — a rename never changes it — and target_network_id says which network a node target belongs to.
Descriptive context on an entry
An entry may carry a metadata object, from router 0.4.127, present only when there is something to put in it. It holds the numbers a system entry destroyed — auto_reload_trigger_cents and auto_reload_amount_cents on billing.credit_auto_reload_cleared, spend_cap_cents on billing.spend_cap_cleared — and the client name and version a caller asserted about itself.
A billing entry says how much, from router 0.4.130, not only that something happened. Router 0.4.118 split billing.credit into one action per operation, so reading a balance and charging a card stopped being the same row; this is the other half — a billing.credit_buy row that could not say whether it was $5 or $100 answered neither of the two questions this log is actually asked, who set the cap to $0 and who bought $100.
| Action | What metadata carries |
|---|---|
billing.credit_buy | amount_cents — including on a refused charge, where it is what was attempted |
billing.credit_auto_reload | auto_reload_trigger_cents and auto_reload_amount_cents, or auto_reload_off when the standing rule was switched off |
billing.credit_spending | credit_enabled |
billing.spend_cap | spend_cap_cents, or spend_cap_reset when the maximum was cleared back to its $0 default — an Owner who deliberately typed 0 did a different thing from one who cleared the field, and both leave the organization bound by $0 |
billing.checkout | plan and interval |
The amount is recorded before the operation runs, which is what puts an attempted and refused charge on the record with the figure attached. The names are shared with the entries a cancellation writes — billing.credit_auto_reload and billing.credit_auto_reload_cleared use the same two keys, and both spend-cap entries use spend_cap_cents — so a rule being armed and the same rule being erased by a cancellation are two rows you read together.
A file entry says which of a machine's two filesystems it touched, from router 0.4.177. A call against a machine's storage area and a call against the machine's own disk both carry target type node and the same id, and they are not the same act: the storage area is bytes noBGP holds, and no owner veto on the machine stands in front of a write there. tree: "storage" marks the first; absent means the machine's own disk. It appears on node.file and on the per-op node.fs_read / fs_write / fs_list / fs_stat / fs_delete / fs_mkdir / fs_edit / fs_copy entries.
A copy names its source as well as its destination, from router 0.4.177. A copy has two ends and the entry's target is the destination — the end the call changed — so without this the source was on no row at all. source_type is node or network, source_id is that id, and source_tree: "storage" says the source was a storage area rather than a machine's disk. They are ids and fixed words, never paths: a path is an argument, and arguments stay off the row.
⚠ It is not backfilled and cannot be. The amount lives in the request and is gone once the call is served, so entries written before router 0.4.130 are missing it permanently.
⚠ Never treat it as a predicate. client_name and client_version are whatever the caller said they were; they are context for a row somebody is already reading, and never an authentication signal. Tool arguments are still not recorded, here or anywhere else: what a billing row carries is a fixed set of amounts and settings, never a card number, a payment-method id, or any free text the caller chose.
Not every call appears. Absent from the log when a member makes it: the fleet-wide reads that address no single thing (network_directory, whoami, service_check), the event-bus tools (event_tail, event_publish, event_subscriptions, event_unsubscribe), service_share, and node_grant / node_revoke / node_label.
A staff action says so on the row. Every use of noBGP staff access is recorded against the organization it reached, marked privileged, and from router 0.4.179 the row also says which basis admitted it — the organization's "Allow noBGP support" setting, or a temporary grant. See noBGP staff access.
Six of the calls above are recorded when noBGP staff make them, from router 0.4.184 — the question the row answers is what did noBGP staff do in our organization, which is worth a row even where the same call from a member is not:
| Action | The staff call |
|---|---|
org.directory_read | Staff read your organization's whole directory (network_directory naming your organization). A directory read of their own organizations writes nothing |
service.check | Staff probed one of your services with service_check |
service.share, node.grant, node.revoke, node.label | Staff tried to change who may visit a service, turn a node's local MCP server on or off, or change a node's labels. Staff never control a node or a service, so these rows are refusals: ok: false with the refusal code |
A call that your organization refuses because "Allow noBGP support" is off is recorded too, with ok: false. A member's call of any of the six still writes nothing, refusals included, and whoami and the event-bus tools write nothing for anyone.
⚠ Router 0.4.98 wrote no entries at all, of any kind — not the node calls it had just added, and not the changes the log had always held. A defect in the audit record's own shape refused every insert, so a log that looks as though it stopped mid-August on an organization that was still busy is that, and not a quiet fleet. It is fixed in router 0.4.99, and the missing entries cannot be recovered or backfilled — they were never written. Calls themselves were unaffected: every one ran and returned normally, so what was lost is the history and not the work.
⚠ Before router 0.4.98 the log recorded only the changes. An organization whose fleet was being read, written and driven all day therefore had a history that looked close to empty — the entries were a network created, a service published, a member's role altered, and nothing about the work itself. Entries written before that release are unaffected and nothing is backfilled. The other side of the change: the log now grows with your fleet's activity rather than with its changes, so an automation loop paging a file in 32 KiB reads writes one entry per read.
How a node call is attributed
A call that names a node is filed against the organization that owns that node, not against the caller's own — that is the organization with a reason to ask what was done to our machines. The entry's target type is node and its target id the node's id.
A call that never resolves its target — a mistyped node name, or a node in an organization you are not in — has no organization to be filed against and is not recorded.
Single Sign-On (SSO)
Organizations can require members to sign in through the company identity provider. Both OIDC and SAML providers are supported.
Setup is Owner-only:
- Connect an identity provider — the Owner opens an ephemeral admin-portal link and completes the connection (see
org_sso_setupin the MCP Reference). - Optionally turn on enforcement — members must then log in via SSO; password and social logins are rejected. Enforcement can only be enabled after a provider is connected, so an organization can't lock itself out.
Members signing in through the organization's IdP for the first time are provisioned automatically.
Enforcement covers signing in from a terminal, from router 0.4.186. nobgp login and browser registration complete through the same sign-in page as the app, and a password login there is now refused the same way: the browser is returned to the noBGP sign-in page with error=sso_required and nothing is handed to the terminal. Automatic provisioning covers that door too, so a first SSO sign-in from a terminal joins the organization as one from the app does. Registering a machine with an enrollment key is unaffected — no person signs in.
Billing
Plans, allowances, and usage are all per organization — see Plans & Billing.