From 73d927468e6abca6039a3758a9365f271285b925 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Nov 2026 15:52:56 +0000 Subject: [PATCH] docs(deploy): Enterprise licensing and multi-organization pages Two Enterprise-only pages under Deploy, written from objectstack-ai/cloud main: licensing (obtaining, online vs air-gap, the boot banner, renewal, subscription vs perpetual lapse, Enterprise Single-Node vs Enterprise) and multi-organization (postures, the licence gate and its refusal, run shapes, the walled-posture decisions, Community image, Cloud for small scale). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012dn2AdARJtc4RgABPH7PHk --- content/docs/deploy/licensing.mdx | 299 +++++++++++++++++++++ content/docs/deploy/meta.json | 2 +- content/docs/deploy/multi-organization.mdx | 146 ++++++++++ 3 files changed, 446 insertions(+), 1 deletion(-) create mode 100644 content/docs/deploy/licensing.mdx create mode 100644 content/docs/deploy/multi-organization.mdx diff --git a/content/docs/deploy/licensing.mdx b/content/docs/deploy/licensing.mdx new file mode 100644 index 00000000..e44c55b2 --- /dev/null +++ b/content/docs/deploy/licensing.mdx @@ -0,0 +1,299 @@ +--- +title: Licensing +seoTitle: "Enterprise Licensing: Obtain, Validate, Renew" +description: How an ObjectOS Enterprise license is issued, validated online or air-gapped, reported in the boot banner and renewed, what Enterprise Single-Node and Enterprise each unlock, and what happens when a license ends. +editions: [enterprise] +--- + +An ObjectOS Enterprise deployment runs the licensed Enterprise image with +a license in `OS_LICENSE_KEY`. The license decides what the image unlocks. +This page covers how you get one, the two ways it is validated, how to read +what the runtime decided, how to renew, and what a license that ends +switches off. Contract options are on +[License & Pricing](/docs/editions/license). + +## Obtaining a license + +Licenses are issued by ObjectStack under contract: +[contact sales](mailto:sales@objectos.ai). There is no self-serve +checkout. Your license mail contains: + +1. **The image digest** of your release. Install by digest, because a tag + can be moved and a digest cannot ([why](/docs/deploy/docker#pin-the-image-by-digest)). +2. **A registry pull token** with package read access. You do not need it + when you install from an offline bundle. +3. **The license**: one line of text, signed by ObjectStack. The runtime + verifies the signature at boot against a public key built into the + image, so a license with any byte changed is refused as invalid. +4. **The deploy bundle**, with the Compose stack, the environment templates + and the scripts for your release. + +Store the license as a secret, the same way you store `OS_AUTH_SECRET` +([Production Readiness](/docs/operate/production)). Three things are fixed +when the license is issued, and none of them is a setting you can change: + +- its **edition**: Enterprise Single-Node or Enterprise; +- its **mode**: online or air-gap ([below](#online-or-air-gap)); +- its **kind**: a subscription with an end date, or a perpetual license + with a maintenance end date ([below](#when-a-license-ends)). + +## Enterprise Single-Node and Enterprise + +Enterprise Single-Node (formerly Business Self-Managed) is the single-node +entry option of ObjectOS Enterprise. Its license carries a fixed set of +features; everything else on the list below needs an Enterprise license. + +| | **Enterprise Single-Node** | **Enterprise** | +|---|---|---| +| Nodes | One (`maxNodes: 1`) | Several, up to the count your license carries (`maxNodes`) | +| AI seats | Up to 25 | Set by your contract (`maxAiSeats`) | +| License mode | Online only | Online or air-gap | +| In-product AI | ✓ | ✓ | +| AI governance | ✓ | ✓ | +| Hierarchy security | — | ✓ | +| Several organizations on one deployment | — | ✓ | +| SCIM 2.0 provisioning, on by default | — | ✓ | +| Customer-managed keys for secrets | — | ✓ | +| When the license ends | Paid features switch off | Paid features switch off; a perpetual license keeps the versions released during maintenance | + +What each row means, and where it stops: + +- **In-product AI.** The "ask" agent is the included, supported agent. The + AI Builder ships as a **preview** that is not part of the contract, and + the boot banner labels it so. An administrator assigns AI seats to users; + the license sets how many. You bring your own model and key. +- **AI governance.** An audit trail of every AI tool call (who asked the AI + what, and what it touched), permission checks on each answer so the AI + cannot become a way around record permissions, masking of sensitive + fields in the data the AI's tools fetch, and visible quota refusals. + Conversation history a user pastes in is not re-masked. +- **Several nodes.** The license unlocks a multi-node topology up to its + node count; it is not a high-availability product. The deploy bundle runs + one node today, and the runtime refuses more than one replica until a + cluster driver is configured. Multi-node is set up with us under your + contract. Without a license, the runtime runs one replica, and a + deployment that declares more refuses to start. +- **Hierarchy security.** Record visibility relative to the business-unit + tree: the unit, unit-and-below and own-and-reports read scopes + ([Hierarchy Visibility](/docs/configure/permissions/hierarchy-visibility)). +- **Several organizations.** The per-organization isolation wall on one + database. See [Multi-organization](/docs/deploy/multi-organization). +- **Customer-managed keys.** Secret fields, datasource credentials and + encrypted settings are sealed under your own AWS KMS, Azure Key Vault or + HashiCorp Vault (transit) key, by envelope encryption: each value gets a + fresh data key, wrapped by your key, and your key never leaves your KMS. + Revoking the key makes those secrets unreadable at once. A KMS that is + configured but cannot be reached, or is not licensed, **refuses the + boot**; the runtime never falls back to the local key. Business fields, + metadata, attachments and audit logs are not encrypted by this feature; + at rest they rely on your disk or database encryption. It applies to the + single-environment and composed shapes and is refused on the + artifact-pinned shape. Choose it at installation: secrets written before + you enable it are not migrated, and turning it off makes the secrets it + sealed unreadable. Customer-managed keys ship in Enterprise releases + newer than 4.3.1. + +## Online or air-gap + +A license's mode says how it is validated. Both modes verify the signature +and the end date locally first. + +| | **Online** (the default) | **Air-gap** | +|---|---|---| +| Validation | At boot, then at the control plane: the license is exchanged for a 72-hour entitlement, refreshed every 24 hours | Locally only, against the key in the image. No network traffic. | +| `OS_CLOUD_URL` | Your control plane, with the deployment's cloud binding completed | `off` | +| When the control plane cannot be reached | The deployment stays licensed for **7 days** from the last successful validation, then runs as Community until validation succeeds again | Does not apply | +| Available for | Enterprise Single-Node and Enterprise | Enterprise only | + +Two combinations are decided for you at startup: + +- An online license with `OS_CLOUD_URL=off` can never be validated, so the + runtime **refuses to start**, naming both settings: + `✖ FATAL: this deployment has an ONLINE-mode OS_LICENSE_KEY but no control plane to validate it against`. + Ask for an air-gap license for a disconnected site. +- The composed shape, where several organizations share one database behind + a published app, accepts **only** an air-gap license with + `OS_CLOUD_URL=off`. + +[Air-gapped Deployment](/docs/deploy/air-gapped) has the full matrix and the +reasons behind it. + +On the first boot with an online license, before any entitlement is cached, +the runtime waits up to 10 seconds for the control plane, so the Enterprise +features can mount on that boot. If validation succeeds later than that, the +runtime is validated but was already wired as Community: the log says so, +and the Enterprise features mount on the next restart. Later restarts inside +the grace window are licensed from the cached entitlement without waiting. + +## What the boot banner shows + +At every boot the runtime prints a banner to its log that states what it +decided. A licensed deployment prints, for example: + +```text +──────────────────────────────────────────────────────────────── + ObjectOS Enterprise — LICENSED + customer : Example Corp + customerId: cus_0123 + org : (none) + licenseId : lic_0456 + seats : up to 25 AI seat(s) + nodes : 1 + expires : 2027-06-30T00:00:00.000Z + features : aiStudio, governance + in-UI AI : ENABLED (AI Builder + ask) + preview : AI Builder(预览,不在合同范围内); ask is the included, supported agent + mode : ONLINE (validated by the control plane) + validated : 2026-11-07T09:12:44.000Z + ent expiry: 2026-11-10T09:12:44.000Z + refresh : 2026-11-08T09:12:44.000Z + runtimeId : rt_0789 +──────────────────────────────────────────────────────────────── +``` + +| Line | What it tells you | +|---|---| +| `seats`, `nodes` | The AI-seat and node counts the license carries; `unlimited` when it carries none | +| `expires` | A subscription's end date; for a perpetual license, `never (perpetual license — see maintenance below)` | +| `features` | The features the license grants: `aiStudio` (in-product AI), `governance`, `clustering`, `hierarchySecurity`, `scim`, `multiOrg`, `customerManagedKeys` | +| `in-UI AI` | Which agents this process actually mounts. When `OS_AI_STUDIO_AGENTS` narrows them, the line names what is withheld and by which variable. | +| `preview` | The AI Builder is a preview, outside the contract (the label is printed in Chinese and reads "preview, not covered by the contract") | +| `mode` … `runtimeId` | Online licenses only: the last successful validation, when the current entitlement expires and when it is refreshed next. An air-gap license prints none of these lines. | + +Some states lead the banner, so they are the first thing you read: + +- `⚠ GRACE — the control plane has not confirmed this license recently`, + followed by a `degrades` line with the date the grace window ends. +- For a perpetual license, a `license` line names its state: + `PERPETUAL — in maintenance until …`, or + `PERPETUAL — VERSION-LOCKED (maintenance ended …)` with + `updates : none — newer images run as Community; renew maintenance to update`. + `⚠ BUILD STAMP UNUSABLE` means the image cannot prove its release date + ([below](#perpetual)). + +A deployment without a usable license prints the Community banner instead, +with the reason and the remedy: + +```text +──────────────────────────────────────────────────────────────── + ObjectOS Community mode — NO valid Enterprise license + reason : no license (OS_LICENSE_KEY unset) + in-UI AI : DISABLED (AI Builder + ask) + data : available via the open MCP server — bring your own AI client + to enable : set OS_LICENSE_KEY to a valid Enterprise license token +──────────────────────────────────────────────────────────────── +``` + +On the artifact-pinned shape (`OS_ARTIFACT_URL`) the image's own +configuration does not run, so there is no banner, and none of what it +reports (AI seats, AI governance, hierarchy security) mounts either. + +### Check a license without booting + +The deploy bundle's `license-check.sh` runs the image's own verifier in a +container with no network, and sends nothing anywhere. It prints the +customer, edition, term, mode and limits, never the license itself, and +exits 0 only when this image will boot licensed with your `.env`: + +```bash +./license-check.sh # the license in .env, against the running image +./license-check.sh --file new.txt # a license you have not stored yet +./license-check.sh --image # against the image you are about to upgrade to +``` + +The edition reads `enterprise`, or `business` for Enterprise Single-Node +(its former name). Thirty days before a subscription ends, the check warns. + +## Renewal + +A renewal is a new license: a new line of text with a later end date, for +both modes. Install it before the current one ends: + +```bash +./license-check.sh --set ./renewal.txt # stores it in .env as OS_LICENSE_KEY and checks it +docker compose up -d # applies it +``` + +On Kubernetes, update the Secret that holds the license and restart the +pods. A license is deployment-wide: every replica runs the same one +([Kubernetes](/docs/deploy/kubernetes#enterprise-adds)). + +For a perpetual license, renewing **maintenance** is what keeps updates +coming: it is a license with a later maintenance end date, needed before you +upgrade to an image released after your current one. + +## When a license ends + +A license ends in one of two ways, by its kind. Neither stops the runtime +or touches your data, with the three exceptions [below](#three-deployments-refuse-to-start-instead). + +### Subscription + +Every Enterprise Single-Node license is a subscription, and so is an +Enterprise license unless your contract says perpetual. At its next start +after the end date, the runtime treats the license as absent and runs as +Community: + +- the in-product AI and AI governance are off; +- the hierarchy read scopes grant nothing; +- SCIM is no longer on by default; +- your data stays intact, and the open MCP server keeps working, so your + own AI client still reaches it. + +A subscription does not version-lock: no image keeps the Enterprise features +after the end date. Installing a renewal license restores them on the next +start. + +### Perpetual + +A perpetual license is a contract-only option of Enterprise; it is not +offered for Enterprise Single-Node. It has no end date. It has a +**maintenance end date** instead, and keeps the versions released while +maintenance was paid: + +- An image released **before** the maintenance end keeps every Enterprise + feature, the in-product AI included, for as long as you run it. After the + maintenance end, the banner and `license-check.sh` say `VERSION-LOCKED`. +- An image released **after** the maintenance end runs as Community. Before + an upgrade, run `./license-check.sh --image `: it says whether + that image is covered, and `./upgrade.sh preflight` stops when it is not. +- Updates, patches and support end with maintenance. + +The image proves when it was released through its release manifest, +`/etc/objectstack/ee-release.json`. Do not mount anything over that file or +its directory: the runtime then cannot use it, and the license lapses at the +maintenance end like a subscription. An online perpetual license still needs +its control plane after maintenance ends, with the same 7-day grace window; +ask for an air-gap perpetual license if the deployment must not depend on +it. The `VERSION-LOCKED` states in the banner and in `license-check.sh` ship +in Enterprise releases newer than 4.3.1; an older image boots licensed with +a perpetual license. + +### Three deployments refuse to start instead + +Three configurations need a valid license to exist at all, so a license that +has ended, or is missing, makes them **refuse to start** rather than run as +Community. The data is still intact; a valid license brings them back. + +- **Several organizations** (`OS_TENANCY_POSTURE=isolated` or `group`): + isolation between organizations is licensed, and a deployment that asked + for it never serves traffic without it. See + [Multi-organization](/docs/deploy/multi-organization#the-license). +- **Customer-managed keys** (a KMS set in `OS_CRYPTO_PROVIDER`): the runtime + never falls back to the local key, because the secrets were sealed under + yours. +- **Several nodes** (`OS_CLUSTER_REPLICAS` above 1): without a + multi-node license the runtime refuses the declared topology rather than + run replicas that do not coordinate. + +Plan renewals for these the way you plan a certificate renewal: the +license check warns 30 days ahead, and the date is in every boot banner. + +## Next + +- [Multi-organization](/docs/deploy/multi-organization) — the license on a + deployment several organizations share. +- [Air-gapped Deployment](/docs/deploy/air-gapped) — the license mode for + disconnected sites. +- [Docker → Enterprise adds](/docs/deploy/docker#enterprise-adds) — the + registry, verification and upgrade steps the license comes with. diff --git a/content/docs/deploy/meta.json b/content/docs/deploy/meta.json index 1a866560..87a13a46 100644 --- a/content/docs/deploy/meta.json +++ b/content/docs/deploy/meta.json @@ -1,5 +1,5 @@ { "title": "Deploy", "defaultOpen": false, - "pages": ["index", "docker", "kubernetes", "air-gapped"] + "pages": ["index", "docker", "kubernetes", "air-gapped", "licensing", "multi-organization"] } diff --git a/content/docs/deploy/multi-organization.mdx b/content/docs/deploy/multi-organization.mdx new file mode 100644 index 00000000..5dd2a397 --- /dev/null +++ b/content/docs/deploy/multi-organization.mdx @@ -0,0 +1,146 @@ +--- +title: Multi-organization +seoTitle: "Multi-organization Self-Managed Deployments: License and Postures" +description: Several organizations on one self-managed deployment is an ObjectOS Enterprise capability — the postures, the license it needs, the refusals at startup, and when ObjectOS Cloud is the better fit. +editions: [enterprise] +--- + +A self-managed ObjectOS deployment serves **one organization** unless you +ask for more. Running several organizations on one deployment, against one +database, is an **ObjectOS Enterprise** capability: it needs an Enterprise +license that grants multi-organization, and the runtime refuses to start +without one. Enterprise Single-Node does not include it. + +## Which edition fits + +| You want | Use | +|---|---| +| One organization on your own infrastructure | ObjectOS Community, or any Enterprise license. This is the default. | +| Several organizations on your own infrastructure, one database | **ObjectOS Enterprise**, with a license that grants multi-organization | +| A few organizations, without operating a deployment | **ObjectOS Cloud**. Each organization signs up on its own plan, and we run the isolation. See [Plans and Seats](/docs/cloud/plans-and-seats). | +| Organizations that must not share a database | One deployment each, on any edition | + +## Postures + +`OS_TENANCY_POSTURE` says whether organizations are walled off from one +another: + +| Posture | Meaning | +|---|---| +| `single` | One organization owns the deployment. There is nothing to wall off. | +| `group` | Several organizations share one dataset, and group members read across them. | +| `isolated` | The hard wall: a member of one organization cannot tell another exists. | + +`group` and `isolated` are the **walled** postures. Two things about the +setting itself: + +- **Unset means `single`, silently.** A deployment that never sets the + posture runs one organization and reports nothing unusual, because from + the runtime's point of view nothing is wrong. Write the posture you want. +- **A misspelled value refuses the boot** rather than fall back to a posture + with no wall. + +## The license + +A walled posture needs an Enterprise license that grants `multiOrg`. An +Enterprise license grants it unless your contract lists features +explicitly; an Enterprise Single-Node license never does. The boot banner's +`features` line shows what yours grants +([Licensing](/docs/deploy/licensing#what-the-boot-banner-shows)). + +Without it, the runtime **refuses to start** and says why in its first line: + +```text +✖ FATAL: tenancy posture 'isolated' requires an Enterprise license. + + Multi-organization isolation is a LICENSED Enterprise capability (ADR-0022, + cloud#766). This runtime is not licensed for it, so the organization wall + cannot be activated — and a deployment that asked for isolation must never + serve traffic pretending to have it. Refusing to boot. +``` + +It is a licensing failure, not a missing package, and the message says so. +It offers two fixes: a license that grants multi-organization, or +`OS_TENANCY_POSTURE=single`. `OS_ALLOW_DEGRADED_TENANCY` does not apply to +it. The refusal is the same on every run shape, including the +artifact-pinned one, where it carries the code `MULTI_ORG_NOT_LICENSED`. + +The same holds when a license **ends**: a walled deployment does not fall +back to Community, as a single-organization one does. It refuses to start +at its next boot until a valid license is installed, and its data stays +intact. The deploy bundle's `license-check.sh` reports that refusal ahead of +time when your `.env` asks for a walled posture. A perpetual license keeps a +walled deployment running on the images it covers. +[Licensing](/docs/deploy/licensing#when-a-license-ends) has the details. + +### On the Community image + +The published Community image runs one organization. It does not ship the +multi-organization runtime, so a walled posture there refuses to start, +saying that `@objectstack/organizations` could not be loaded. That refusal +offers `OS_ALLOW_DEGRADED_TENANCY=1` as a way past; it boots with the wall +**inactive**, and organization boundaries are not enforced. Do not use it to +run several organizations. + +The multi-organization runtime itself is open source in the ObjectStack +framework. On ObjectOS, the edition that runs several organizations on one +self-managed deployment, with a license and our support behind it, is +Enterprise. + +## Run shapes + +| Shape | Several organizations | +|---|---| +| **Config-authored** (the image's own app, the Compose default) | Supported, with a walled posture and a license that grants it | +| **Composed** (a published app with this image's enterprise plugins) | Supported, and the shape built for it. It is **air-gap-licensed only** and requires `OS_CLOUD_URL=off`, both enforced at startup ([Air-gapped](/docs/deploy/air-gapped#the-hosted-multi-organization-shape-is-air-gap-licensed-too)). | +| **Artifact-pinned** (`OS_ARTIFACT_URL`) | Still licensed the same way, but the image's configuration does not run: no AI governance, no AI seats, and the AI **build** agent cannot be closed. Use the composed shape instead. | + +Why the build agent matters: metadata is scoped to the deployment, not to +an organization. On a shared database, one organization's AI Builder turn +rewrites the schema every other organization runs on. On the composed shape, +`OS_AI_STUDIO_AGENTS=ask` closes it. + +## Decisions a walled deployment must declare + +On a walled posture the runtime **refuses to start until you decide** three +more things. Declaring any available value is accepted; not deciding is not. + +| Variable | Decides | +|---|---| +| `OS_AUTH_MEMBERSHIP_POLICY` | What a new sign-up joins. Where a membership *is* the tenant boundary, `invite-only` is the usual answer. | +| `OS_AI_STUDIO_AGENTS` | Which AI agents are mounted: `ask`, `build`, or both. `ask` is the usual answer, for the reason above. | +| `OS_PLATFORM_OWNER_EMAIL` | Who the platform administrators are. Under a walled posture it is the only source of that standing. | + +[Environment Variables](/docs/reference/environment-variables#multi-organization-deployments) +describes each in full. A single-organization deployment sees none of these +checks. + +## What the wall separates + +The wall separates the organizations' **data and memberships**. It does not +separate metadata, which belongs to the deployment. Isolation stronger than +the wall is deployment separation: an organization that must have its own +database gets its own deployment. + +Two Enterprise protections apply per organization on a walled deployment: + +- **SSO domain verification is on by default** in the Enterprise image. An + identity provider an organization registers signs no one in until a + platform administrator has proved the organization controls its domain, + with a DNS TXT record. Without it, one organization's provider could + create accounts for any email address it asserts, another organization's + domain included. `OS_SSO_DOMAIN_VERIFICATION=false` turns it off. The + default applies on the config-authored and composed shapes, not on the + artifact-pinned one, and ships in Enterprise releases newer than 4.3.1. +- **Customer-managed keys** work on the config-authored and composed shapes, + so a walled deployment can seal its secrets under your own KMS key + ([Licensing](/docs/deploy/licensing#enterprise-single-node-and-enterprise)). + +## Next + +- [Licensing](/docs/deploy/licensing) — obtaining, validating and renewing + the license this page depends on. +- [Troubleshooting](/docs/operate/troubleshooting) — the startup refusals, + told apart. +- [Production Readiness](/docs/operate/production) — the walled-posture + items on the go-live checklist.