DocsGet started
Editions and capabilities
From docs/CAPABILITIES.md in the repository.
On this page
D7 in DEVELOPMENT_PLAN.md: an organization has one edition (community, cloud or enterprise). Each edition maps to
a set of capability names in apps/api/core/capabilities.py. Features check a capability name and never the edition;
tests/test_capabilities.py fails if code outside core/capabilities.py, core/limits.py (the per-edition limit
defaults) and core/models.py compares an edition.
Mapping
| Capability | Community | Enterprise |
|---|---|---|
catalog (applications, components, integrations, people, teams, vendors) | yes | yes |
relationships (relationships, dependencies, change assessments) | yes | yes |
reviews (record reviews, staleness) | yes | yes |
renewals | yes | yes |
notifications.digest (daily digest, Needs attention snoozes) | yes | yes |
discovery.entra | yes | yes |
imports (CSV import and export) | yes | yes |
api.tokens | yes | yes |
audit.log (in-app audit log, its CSV export) | yes | yes |
sso.oidc (OpenID Connect single sign-on: Entra ID, Okta, Google Workspace, any OIDC provider) | yes | yes |
ai_agents (AI agents, AI providers and models, the models-in-use report) | yes | yes |
custom_fields (custom field definitions and values) | yes | yes |
attachments (files and links on records and vendors) | yes | yes |
data_quality (data-quality rules, exceptions, the completeness score and the Data quality queue) | yes | yes |
contracts (contracts, contract lines and license pools) | yes | yes |
webhooks (signed event webhooks: endpoints, subscriptions, the delivery log) | yes | yes |
vendor_risk (the vendor risk file: profile, risk documents, their expiry reminders, the risk overview and its gaps) | yes | yes |
org.export (export all of the organization’s data as a zip) | yes | yes |
eol (end-of-life tracking of on-prem software versions via endoflife.date) | yes | yes |
catalog_code (catalog-as-code: the catalog as YAML files in git) | yes | yes |
data_flows (the data-flow map: where classified data goes, risky flows, its graph and CSV) | yes | yes |
ticket_links (Jira, ServiceNow and Halo ticket links on assessments and renewals, their status refresh and opt-in ticket creation) | yes | yes |
org.tenant_backups (a nightly export of the organization kept for SOFTWARDEN_TENANT_BACKUP_RETENTION nights; CLOUD_AND_ENTERPRISE) | yes | |
sso.saml (SAML 2.0 single sign-on, ee/saml) | yes | |
scim (SCIM 2.0 provisioning of users and groups, ee/scim) | yes | |
assistant (Claude suggests link / create / dismiss for discovery candidates) | yes | |
discovery.azure | yes | |
discovery.powerplatform | yes | |
discovery.halo | yes | |
audit.export (the audit log as JSON lines for a SIEM, with snapshots) | yes | |
compliance_profile | yes |
Community lists only features that exist; a phase that ships a community feature adds its name. A gated capability
without a real endpoint is tested through a test-only route (tests/capability_urls.py, never in the shipped URLconf);
when the feature ships, its entry in COVERAGE in tests/test_capabilities.py moves to the real endpoint. Adding a
name to the enterprise or cloud set without a COVERAGE entry fails the suite.
Every route is held to its capability too (F-66): ROLE_LEDGER in tests/test_permissions.py gives every
organization route, every method, its minimum role and its capability decision (- for none), so a new route fails
until it has a line. tests/test_capabilities.py fails a route that declares a capability its line lacks, and calls
every route whose line names one as the Admin of an organization without it (a community organization, or a platform
revoke for a community capability): it must answer the capability problem naming it.
The plan’s enterprise list also names retention (configurable retention for audit events and notification logs).
It isn’t built, so it isn’t in the enterprise edition until it is (release hardening C8): audit events are kept
forever in every edition. AuditEventQuerySet.purge() stays as the deletion that feature will use (today only
seed_scale calls it). A platform override naming retention stored before this is ignored.
In the API
-
GET /api/v1/auth/mereturnseditionandcapabilities(sorted) for the current organization. The SPA gates oncapabilities. -
A gated request in an organization without the capability gets 403:
{ "type": "/problems/capability-required", "title": "This feature isn't enabled for this organization.", "status": 403, "detail": "This needs the discovery.azure capability, which this organization's edition doesn't include.", "capability": "discovery.azure" }The role is checked first, so a Viewer refused for role gets the role message.
-
In code:
require_capability(org, name)after the role check, orhas_capability(org, name)to branch. An unknown name raisesValueError, so a typo fails in tests rather than silently refusing.
Setting the edition
Only an operator sets it; no API endpoint writes it:
docker compose exec app python manage.py set_edition --list # slug, edition, name
docker compose exec app python manage.py set_edition <org-slug> enterprise # audited as organization.edition_changed
On a development checkout run the same commands in apps/api as uv run python manage.py ..., with
SOFTWARDEN_DEBUG=1 set (README, Run it locally).
Platform staff can do the same from the platform admin console (POST /api/v1/platform/organizations/{id}/edition,
with a reason; audited in the organization and in the platform stream).
Per-organization overrides (feature flags)
Platform staff can grant or revoke one capability for one organization, with a reason and an optional expiry
(POST /api/v1/platform/organizations/{id}/capability-overrides; Phase 10, runbook
docs/04-development/platform-admin-runbook.md). capabilities(org) is then the edition’s set, plus live grants,
minus live revokes, and every gate, auth/me included, follows it. The name must be one in the mapping above. The
overrides are read once per request or job and a change applies from the next request, well within a minute.
Overrides change names only; nothing compares editions.
Preview features
1.0.0 ships some features as previews, hidden unless the installation turns them on (owner decision 2026-11-10,
launch scope): Okta and Google Workspace discovery, and ServiceNow and Halo ticket connections. One installation
setting, SOFTWARDEN_PREVIEW_FEATURES (docs/ENV.md; off by default; Compose .env, Helm extraEnv), and one place
that reads it, capabilities(org) in core/capabilities.py:
- Every edition includes
preview. While the setting is off,capabilities(org)dropspreviewand the capabilities that exist only as previews (PREVIEW_ONLY:discovery.okta,discovery.google) for every organization, after its edition and overrides, so a platform grant can’t bring them back. With it on, they follow the edition and overrides as usual (Okta and Google stay enterprise), and a platform revoke ofpreviewhides them for one organization. - Every gate then follows:
auth/me, the routes (403 /problems/capability-required, whose detail says it is a preview feature this installation hasn’t turned on), the services, the schedules and the SPA, which hides the Okta and Google sources, setup cards and connection tabs and the ServiceNow and Halo ticket connections whenpreviewis missing, rather than calling them another edition’s. - Nothing is deleted. Okta and Google connectors stay in
GET /connectorswithavailable: false(as any connector whose source the organization lost): they can be turned off or deleted, never changed, run, checked or scheduled; their runs and observations stay; doctor reports them as “Unavailable (preview feature off)”, the Discovery summary marks themavailable: false, andGET /discovery/sourcesleaves the Okta and Google sources out. Okta and Google connections stay in the database, unreachable through the API. ServiceNow and Halo ticket connections are listed withavailable: false(the admin page: “Kept from a preview install”) and can only be deleted, which is audited withkept: true, so a kept Halo one never blocks deleting its Halo connection. ServiceNow and Halo links stay and new ones can be pasted, with a status set by hand. Turning the setting on gives the capabilities back and all of it works again, unchanged. - To release a preview feature, take its name out of
PREVIEW_ONLY(and, for a ticket system, set its entry intickets/services.SYSTEM_CAPABILITYto None).
Limits
Beside what an organization may use, core/limits.py caps how much it may create: records, other_records (people, vendors, contracts and the other secondary rows, together), connectors, members
(active), attachment_bytes, and its API rates api_rate_read and api_rate_write (requests per minute for the
organization as a whole). Defaults per edition (EDITION_LIMITS, the one place billing tiers will set them):
| Limit | Community | Enterprise |
|---|---|---|
records | unlimited | unlimited |
other_records | unlimited | unlimited |
connectors | unlimited | unlimited |
members | unlimited | unlimited |
attachment_bytes | unlimited | unlimited |
api_rate_read | unlimited | unlimited |
api_rate_write | unlimited | unlimited |
Platform staff override one organization’s value, with a reason and an optional expiry
(POST /api/v1/platform/organizations/{id}/limits, value: null for unlimited; an API rate is at least 1). A create past a limit is
409 /problems/limit-reached naming the limit, its maximum and the usage (reactivating a deactivated member
counts as adding one); other updates and deletes never are, and lowering
a limit removes nothing. Members read their limits and usage at GET /api/v1/organization/limits. docs/API.md
“Limits” lists every create that is checked. The operator’s break-glass recovery (manage.py reset_admin) can
reactivate an account past the members limit; it removes nothing, and new member creates stay refused until usage is
back under the limit.
There is no license verification yet. The organization’s license key slot (licence_key_ciphertext, key id, set at)
exists for when it arrives and is never returned, audited or exported.
Configuring the assistant (enterprise)
The assistant suggests link, create or dismiss for undecided discovery candidates, with a confidence and a reason; Editors decide (Accept, Reject, or any decision as usual) and nothing is ever applied on its own.
- An Admin opens the assistant settings (
GET/PATCH /api/v1/assistant/settings, If-Match supported), enters an Anthropic API key (api_key, write-only; or leaves it empty to use the installation’s key, when the installation offers it), sets the monthly cap (monthly_cap_usd, default 10.00 USD) and turns it on (enabled: true). - Every successful discovery run that sees new or changed objects queues a batch for its connector; an Editor can ask
for one with
POST /api/v1/assistant/batches({"connector": id}, or all).GET /api/v1/assistant/batchesshows what each did and why one stopped (stop_reason: cap_reachedis “cap reached”). GET /api/v1/assistant/usage?month=YYYY-MM(Admin) lists every API call with its four token counts and cost, and the month’s total against the cap.
Installation settings: SOFTWARDEN_ASSISTANT_MODEL (default claude-sonnet-5-5), SOFTWARDEN_ASSISTANT_PRICES
(JSON, USD per million tokens per model: input, output, optional cache_read and cache_write; a model with no
price is refused), ANTHROPIC_API_KEY (the installation’s key, for organizations without their own) and
SOFTWARDEN_ASSISTANT_INSTALLATION_CAP_USD (the most each organization may spend on the installation’s key per month).
The installation key is used only when the ceiling is set; an organization on it is held to the lower of its own cap
and the ceiling (month.cap_usd; installation_key_available and installation_cap_usd in the settings). What is
sent to Anthropic: docs/PERMISSIONS.md, “AI assistant: Anthropic”.