Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
de4596b
docs: fill Workspace.md documentation gaps (#1927)
hl662 Mar 5, 2026
f1848fa
Merge branch 'master' into nam/settings-workspace-docs
hl662 Mar 5, 2026
5d3fd02
Merge remote-tracking branch 'origin/master' into nam/settings-worksp…
hl662 Mar 19, 2026
735f169
Split Workspace.md into 3 focused docs: overview, Settings, and Works…
hl662 Mar 19, 2026
c6d9106
Merge remote-tracking branch 'origin/master' into nam/settings-worksp…
hl662 Mar 20, 2026
097cb41
docs: fix invalid Workspace resources link in WorkspacesAndSettings.md
hl662 Mar 20, 2026
f4e8a30
docs: fix Mermaid diagram syntax in workspace/settings learning docs
hl662 Mar 20, 2026
a017997
docs: fix Mermaid subgraph title clipping in WorkspacesAndSettings di…
hl662 Mar 23, 2026
07cea38
docs: shorten CloudSqlite subgraph title to fix header clipping
hl662 Mar 23, 2026
d2c3604
Merge remote-tracking branch 'origin/master' into nam/settings-worksp…
hl662 Apr 6, 2026
21fe6c9
restructure: separate settings and workspace docs into focused files
hl662 Apr 6, 2026
32a9f15
polish: address doc review feedback for clarity and consistency
hl662 Apr 6, 2026
751fd56
Merge remote-tracking branch 'origin' into nam/settings-workspace-docs
hl662 Apr 7, 2026
31d7e2a
Add missing SettingsDb extract snippets for Settings.md docs
hl662 Apr 8, 2026
6c64046
Fix invalid API hyperlinks in Settings.md
hl662 Apr 8, 2026
6956964
Merge origin/master into nam/settings-workspace-docs
hl662 Apr 8, 2026
10544e2
Replace SettingsDb with 'settings container' across learning docs
hl662 Apr 8, 2026
f06bada
Rename snippet IDs from SettingsDb.* to SettingsContainer.*
hl662 Apr 8, 2026
d37a5f2
Fix three inaccurate claims in Settings.md
hl662 Apr 8, 2026
73d6b15
Merge remote-tracking branch 'origin' into nam/settings-workspace-docs
hl662 Apr 14, 2026
af02b59
Address PR review comments and improve doc coherence
hl662 Apr 14, 2026
d542ac4
Fix doc-review findings: deprecated API refs, extract close(), scope …
hl662 Apr 14, 2026
d5702d8
Remove plan.md
hl662 Apr 15, 2026
1630f5e
changes
hl662 Apr 15, 2026
eed3a1c
Fix scope layering wording and add rush change file
hl662 Apr 15, 2026
fe910db
Merge origin/master into nam/settings-workspace-docs
hl662 Apr 15, 2026
f895423
Fix broken SettingsDb link in 5.8.0 changehistory
hl662 Apr 15, 2026
9caf7f4
Merge branch 'master' into nam/settings-workspace-docs
hl662 Apr 16, 2026
bd75208
Fix no-console lint error in WorkspaceExamples extract
hl662 Apr 16, 2026
8ac9e0d
Merge branch 'nam/settings-workspace-docs' of https://github.com/iTwi…
hl662 Apr 16, 2026
2a341d9
Merge remote-tracking branch 'origin/master' into nam/settings-worksp…
hl662 Apr 17, 2026
37247d0
Trim WorkspacesAndSettings.md per reviewer feedback
hl662 Apr 17, 2026
7b3e690
Add end-to-end tutorial for Settings and Workspaces
hl662 Apr 20, 2026
c0b91fb
Merge remote-tracking branch 'origin/master' into nam/settings-worksp…
hl662 Apr 20, 2026
93a235c
differentiate version pinning from floating settings reference
hl662 Apr 20, 2026
9b82690
Address PR review feedback: remove redundant sections, extract inline…
hl662 Apr 21, 2026
0da070a
Clarify settings container docs
hl662 Apr 22, 2026
d92fc82
Refine settings docs from review
hl662 Apr 22, 2026
6b17077
Apply final settings doc suggestion
hl662 Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "Updated TSDoc for Settings and IModelDb to reference current EditTxn APIs instead of deprecated methods.",
"type": "none",
"packageName": "@itwin/core-backend"
}
],
"packageName": "@itwin/core-backend",
"email": "50554904+hl662@users.noreply.github.com"
}
1 change: 1 addition & 0 deletions core/backend/src/IModelDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2259,6 +2259,7 @@ export abstract class IModelDb extends IModel {
* @param name The name for the SettingDictionary. If a dictionary by that name already exists in the iModel, its value is replaced.
* @param dict The SettingDictionary object to stringify and save.
* @note All saved `SettingDictionary`s are loaded into [[workspace.settings]] every time an iModel is opened.
* @see [[Settings.addDictionary]] to register a dictionary for the current session only without persisting it.
* @beta @deprecated Use EditTxn.saveSettingDictionary instead, within an explicit EditTxn scope (or via withEditTxn). See EditTxn documentation for migration help.
*/
public saveSettingDictionary(name: string, dict: SettingsContainer) {
Expand Down
3 changes: 2 additions & 1 deletion core/backend/src/workspace/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export interface SettingsDictionaryProps extends SettingsDictionarySource {
* [[SettingGroupSchema]]s) at this time.
*
* iModel-specific settings are stored in the iModel's property table and loaded into [[IModelDb.workspace]] when the iModel is first opened.
* You can add and remove a [[SettingsDictionary]] from the property table using [[IModelDb.saveSettingDictionary]] and [[IModelDb.deleteSettingDictionary]].
* You can add and remove a [[SettingsDictionary]] from the property table using [[EditTxn.saveSettingDictionary]] and [[EditTxn.deleteSettingDictionary]].
Comment thread
ben-polinsky marked this conversation as resolved.
*
* iTwin-specific settings are stored in a [[CloudSqliteContainer]] in the cloud.
* When [[IModelHost.getITwinWorkspace]] is invoked, the container is accessed using the iTwinId and the settings are loaded into the returned [[Workspace]].
Expand Down Expand Up @@ -256,6 +256,7 @@ export interface Settings {

/** Add a new [[SettingsDictionary]] with the priority, name, and [[WorkspaceDb]] specified by `props` and setting values supplied by `settings`.
* @note If a dictionary with the same name and [[WorkspaceDb]] already exists, it will be replaced.
* @note Values added via this method exist only for the current session. They are not persisted and will be lost when the host shuts down.
* @see [[addFile]], [[addJson]], and [[addDirectory]] for convenient ways to add dictionaries from various sources.
*/
addDictionary(props: SettingsDictionaryProps, settings: SettingsContainer): void;
Expand Down
2 changes: 1 addition & 1 deletion docs/changehistory/5.8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Previously, settings and binary resources (fonts, textures, templates) were stor

##### Creating a local SettingsDb

See [SettingsDb]($docs/learning/backend/Workspace.md#settingsdb) for full documentation.
Comment thread
hl662 marked this conversation as resolved.
See [Settings containers]($docs/learning/backend/Settings.md#settings-containers) for full documentation.

#### Container type convention

Expand Down
2 changes: 1 addition & 1 deletion docs/changehistory/NextVersion.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ Delete it:

To use iTwin-scoped settings dictionaries, configure [IModelHost.authorizationClient]($backend) and [BlobContainer.service]($backend) so the backend can query and update the iTwin settings workspace container.

See the [Workspace documentation]($docs/learning/backend/Workspace.md) for full details.
See the [Settings documentation]($docs/learning/backend/Settings.md#itwin-settings) for full details on iTwin-scoped settings and the [Workspace documentation]($docs/learning/backend/Workspace.md) for workspace resources.

## Quantity Formatting

Expand Down
418 changes: 418 additions & 0 deletions docs/learning/backend/Settings.md

Large diffs are not rendered by default.

295 changes: 49 additions & 246 deletions docs/learning/backend/Workspace.md

Large diffs are not rendered by default.

110 changes: 110 additions & 0 deletions docs/learning/backend/WorkspacesAndSettings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Workspaces and Settings

Every non-trivial iTwin.js application needs two things at run-time: **configuration** (which tools are available, what units to use, which data sources are active) and **resources** (binary assets like fonts, textures, and templates). iTwin.js provides two complementary systems to address these needs:
Comment thread
ben-polinsky marked this conversation as resolved.
Outdated

- **[Settings]($backend)** — a priority-ordered stack of key-value configuration pairs. Configuration flows from cloud-hosted settings containers into the active [Settings]($backend) runtime, where values can be read by name.
- **[Workspace resources](./Workspace.md)** — versioned binary assets stored in [WorkspaceDb]($backend) containers. Settings tell the application *which* `WorkspaceDb`s to load; the application then retrieves resources from them.

These two systems are deliberately separate. Settings containers are discoverable without opening an iModel. `WorkspaceDb` containers are referenced *by* settings. This separation eliminates the circular dependency that would arise if settings had to be loaded from a `WorkspaceDb` just to discover which `WorkspaceDb` to open.
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated

At runtime, settings and resources are accessed through one of three workspace scopes:

| Workspace | Scope | Access |
|---|---|---|
| [IModelHost.appWorkspace]($backend) | Application-wide defaults and configuration | Available immediately after [IModelHost.startup]($backend) |
| [IModelHost.getITwinWorkspace]($backend) | iTwin-scoped settings shared across all iModels in an iTwin | Requires an iTwinId; no iModel needed |
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated
| [IModelDb.workspace]($backend) | iModel-specific overrides; falls back to `appWorkspace` for unresolved settings. Does **not** automatically include iTwin-scoped settings | Available when an iModel is open |

`appWorkspace` and `IModelDb.workspace` share the same priority stack, so iModel-level settings automatically override application defaults. `getITwinWorkspace` is independent — its settings are only available to an iModel if explicitly referenced (see [Referencing iTwin settings from an iModel](./Settings.md#referencing-itwin-settings-from-an-imodel)). See [Choosing the right workspace](./Workspace.md#choosing-the-right-workspace) for guidance on when to use each scope.
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated

## The two container types
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated

Both container types are built on [CloudSqlite]($backend) with [WorkspaceDb]($backend) as the underlying database, but they serve different roles and are discovered differently.

```mermaid
graph LR
subgraph CloudSqlite["CloudSqlite"]
direction TB
note["Versioned · Immutable once published<br/>Semver-based · Container-scoped"]
end

subgraph SettingsContainer["Settings container (containerType: &quot;settings&quot;)"]
SDB["<b>Settings</b><br/>Key-value config<br/>JSON dictionaries<br/>Priority-based merge"]
end

subgraph WorkspaceContainer["Workspace container (containerType: &quot;workspace&quot;)"]
WDB["<b>Resources</b><br/>Named resources<br/>Strings, blobs, files<br/>On-demand lookup"]
end

CloudSqlite --- SettingsContainer
CloudSqlite --- WorkspaceContainer
SDB -->|"settings point to"| WDB
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated
```

| | **Settings container** | **Workspace container** |
|---|---|---|
| **Purpose** | Application configuration | Data resources |
| **Content** | JSON key-value dictionaries | Strings, blobs, embedded files |
| **Container type** | `"settings"` | `"workspace"` |
| **Discovery** | Automatic via [IModelHost.getITwinWorkspace]($backend) — no iModel needed | Referenced from settings values |
| **Resolution order** | Loaded first | Loaded second, via settings pointers |
| **Write API** | [WorkspaceEditor]($backend) with `containerType: "settings"` | [WorkspaceEditor]($backend) |
| **Versioning** | Semver — immutable once published | Semver — immutable once published |

## How settings and resources connect
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated

At runtime the flow always starts from settings:

1. **Discover and load** — call [IModelHost.getITwinWorkspace]($backend) with an iTwinId. This automatically discovers settings containers for the iTwin, loads their dictionaries into the [Settings priority stack](./Settings.md#settings-priorities), and returns a [Workspace]($backend) ready to use. No iModel is required.
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated
2. **Resolve resources** — read settings values that point to [WorkspaceDb]($backend) containers, then open those containers to access resources.

> For advanced scenarios (admin tooling, custom discovery logic), [WorkspaceEditor.queryContainers]($backend) provides lower-level access to enumerate containers by iTwinId and `containerType`.

## Scope and priority

Settings from multiple sources are merged using a priority stack. A higher-priority dictionary overrides a lower-priority one for any given setting name.

```mermaid
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated
graph TD
subgraph AppWorkspace["IModelHost.appWorkspace"]
D["defaults (100)"]
Comment thread
hl662 marked this conversation as resolved.
Outdated
A["application (200)"]
O["organization/iTwin (400)"]
end

subgraph IModelWorkspace["IModelDb.workspace"]
B["branch (500)"]
M["iModel (600)"]
Comment thread
hl662 marked this conversation as resolved.
Outdated
end

D -->|"overridden by"| A
A -->|"overridden by"| O
O -->|"overridden by"| B
B -->|"overridden by"| M

style M fill:#d4edda,stroke:#28a745
style D fill:#f8f9fa,stroke:#6c757d
```

In practice:
- **Organization-wide defaults** are stored in a settings container and loaded at [SettingsPriority.iTwin]($backend) (400).
- **iModel-specific overrides** are loaded at [SettingsPriority.iModel]($backend) (600) — iModel wins over iTwin.
- **Application defaults** are loaded at [SettingsPriority.application]($backend) (200) — overrideable by any cloud-backed settings.

> **Note:** The diagram above simplifies organization and iTwin into one level. The full priority stack includes a separate [SettingsPriority.organization]($backend) (300) level — see [Settings priorities](./Settings.md#settings-priorities) for details.

`IModelHost.appWorkspace` holds dictionaries at `application` priority or lower. `IModelDb.workspace` holds higher-priority dictionaries and falls back to `appWorkspace` when a setting is not found.

## Container discovery
Comment thread
johnnyd710 marked this conversation as resolved.
Outdated

[IModelHost.getITwinWorkspace]($backend) handles settings container discovery automatically — it queries for containers tagged with `containerType: "settings"`, loads their dictionaries, and returns a ready-to-use [Workspace]($backend). Most application code does not need to interact with the discovery layer directly.

[WorkspaceDb]($backend) containers (`containerType: "workspace"`) are discovered *indirectly* — by reading settings values that point to them. See [Workspace resources](./Workspace.md) for details.

> For admin tooling or custom workflows, [WorkspaceEditor.queryContainers]($backend) provides direct access to enumerate containers by iTwinId and `containerType`. See [Settings containers](./Settings.md#settings-containers) for examples.

## Recommended reading order

1. **This overview** — understand the two systems and three workspace scopes.
2. **[Settings](./Settings.md)** — how to define settings schemas, load dictionaries, read values, and create/manage settings containers in the cloud.
3. **[Workspace resources](./Workspace.md)** — how to create, version, and access binary resources stored in [WorkspaceDb]($backend) containers.
5 changes: 5 additions & 0 deletions docs/learning/backend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ These packages provide the following functions to support backend operations:
- Change Summary
- [Change Summary Overview](../ChangeSummaries)

- Workspaces and Settings
- [Workspaces and Settings overview](./WorkspacesAndSettings.md)
- [Settings](./Settings.md)
- [Workspace resources](./Workspace.md)

For services and app backends:

- Exposing the operations of the backend as RpcInterfaces
Expand Down
2 changes: 1 addition & 1 deletion docs/learning/frontend/Preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

User preferences allow applications to persist settings across a user's sessions, where the settings can be customized by the user of the application. The preferences may be applicable for the application itself or dependent on a specific iTwin or iModel.

User preferences are separate from any iTwin or iModel configuration intended to be shared across multiple users and administered by an admin rather than each individual user. See [Workspace](../backend/Workspace.md) for more details on shared workspace and settings.
User preferences are separate from any iTwin or iModel configuration intended to be shared across multiple users and administered by an admin rather than each individual user. See [Workspaces and Settings](../backend/WorkspacesAndSettings.md) for an overview, [Settings](../backend/Settings.md) for configuration details, and [Workspace resources](../backend/Workspace.md) for binary data assets.
Comment thread
hl662 marked this conversation as resolved.

Examples of user preferences include tool settings; recently accessed models, views, or projects; user interface state; etc.
87 changes: 84 additions & 3 deletions example-code/snippets/src/backend/WorkspaceExamples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ describe("Workspace Examples", () => {

// __PUBLISH_EXTRACT_START__ WorkspaceExamples.QuerySettingDictionary
const hardinessRange = iModel.workspace.settings.getObject<HardinessRange>("landscapePro/hardinessRange");
// returns { minimum: 8, maximum: 10 }
// returns { minimum: 6, maximum: 8 }
defaultTool = iModel.workspace.settings.getString("landscapePro/ui/defaultTool");
// returns "place-koi-pond" as specified by IModelHost.appWorkspace.settings.
// __PUBLISH_EXTRACT_END__
Expand Down Expand Up @@ -262,18 +262,18 @@ describe("Workspace Examples", () => {
// __PUBLISH_EXTRACT_START__ WorkspaceExamples.GetITwinWorkspace
const iTwinWorkspace = await IModelHost.getITwinWorkspace(iTwinId);
const defaultView = iTwinWorkspace.settings.getString("myApp/defaultView");
iTwinWorkspace.close();
// __PUBLISH_EXTRACT_END__
expect(defaultView).to.equal("plan");

// __PUBLISH_EXTRACT_START__ WorkspaceExamples.ReadITwinSettings
const workspace = await IModelHost.getITwinWorkspace(iTwinId);
const defaultViewFromRead = workspace.settings.getString("myApp/defaultView");
const maxItems = workspace.settings.getNumber("myApp/maxDisplayedItems");
workspace.close();
// __PUBLISH_EXTRACT_END__
expect(defaultViewFromRead).to.equal("plan");
expect(maxItems).to.equal(100);
iTwinWorkspace.close();
workspace.close();

// __PUBLISH_EXTRACT_START__ WorkspaceExamples.DeleteITwinSetting
await IModelHost.deleteSettingDictionary(iTwinId, "myApp/settings");
Expand Down Expand Up @@ -431,6 +431,7 @@ describe("Workspace Examples", () => {

const workspaceForTreeDbs = await IModelHost.getITwinWorkspace(iTwinId);
const workspaceTreeDbs = await workspaceForTreeDbs.getWorkspaceDbs({ settingName: "landscapePro/flora/treeDbs" });
workspaceForTreeDbs.close();
// __PUBLISH_EXTRACT_END__
expect(workspaceTreeDbs.length).to.equal(2);

Expand Down Expand Up @@ -536,6 +537,7 @@ describe("Workspace Examples", () => {
await withEditTxn(iModel, async (txn) => txn.saveSettingDictionary("landscapePro/iModelSettings", {
"landscapePro/itwinSettingsRef": settingsSourcesForModelRef,
}));
iTwinWorkspaceForModelRef.close();
// __PUBLISH_EXTRACT_END__

// __PUBLISH_EXTRACT_START__ WorkspaceExamples.OverrideITwinSettingAtIModelLevel
Expand Down Expand Up @@ -567,6 +569,85 @@ describe("Workspace Examples", () => {
// __PUBLISH_EXTRACT_END__
});

it("SettingsDb discover, find, create, update, and read", async () => {
IModelHost.authorizationClient = new AzuriteTest.AuthorizationClient();
AzuriteTest.userToken = AzuriteTest.service.userToken.admin;
const iTwinId = Guid.createValue();

// __PUBLISH_EXTRACT_START__ SettingsContainer.discoverContainers
// Query the BlobContainer service for settings containers associated with an iTwin.
const containerMetadata = await WorkspaceEditor.queryContainers({ iTwinId, containerType: "settings" });
// Each entry includes a containerId and label that can be displayed in an admin UI.
for (const entry of containerMetadata) {
console.log(`Container: ${entry.containerId}, label: ${entry.label}`); // eslint-disable-line no-console
}
// __PUBLISH_EXTRACT_END__
expect(containerMetadata).to.be.an("array");

// __PUBLISH_EXTRACT_START__ SettingsContainer.findContainers
// Find and open settings containers for a given iTwin in a single call.
// This queries the BlobContainer service for settings containers matching the iTwinId,
// requests write access tokens, and opens each matching container.
const settingsEditor = WorkspaceEditor.construct();
const settingsContainers = await settingsEditor.findContainers({ iTwinId, containerType: "settings" });
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
expect(settingsContainers).to.not.be.undefined;
// __PUBLISH_EXTRACT_END__
settingsEditor.close();

// __PUBLISH_EXTRACT_START__ SettingsContainer.createLocal
// Create a new cloud container for settings, write initial values, and publish them.
const editor = WorkspaceEditor.construct();
const container: EditableWorkspaceContainer = await editor.createNewCloudContainer({
metadata: { label: "Project Settings", description: "Settings for this iTwin" },
scope: { iTwinId },
manifest: { workspaceName: "settings", description: "iTwin settings container" },
});

// Acquire the write lock — only one user can hold it at a time.
container.acquireWriteLock("admin");

// Open an editable WorkspaceDb inside the container.
const settingsDb = container.getEditableDb({});

// Write settings as a named resource.
const settings: SettingsContainer = {
"myApp/theme": "dark",
"myApp/maxItems": 50,
};
settingsDb.updateSettingsResource(settings);

// Release the lock to publish the changes.
container.releaseWriteLock();
editor.close();
// __PUBLISH_EXTRACT_END__

// Re-open for update and read tests
const editor2 = WorkspaceEditor.construct();
const containers2 = await editor2.findContainers({ iTwinId, containerType: "settings" });
const container2 = containers2[0];

// __PUBLISH_EXTRACT_START__ SettingsContainer.updateSetting
// Update a single setting without affecting others.
// Acquire the write lock, read existing settings, change one entry, and publish.
await container2.withEditableDb("admin", (db) => {
const current = JSON.parse(db.getString("settingsDictionary") ?? "{}") as SettingsContainer;
current["myApp/maxItems"] = 100;
db.updateSettingsResource(current);
});
// __PUBLISH_EXTRACT_END__

// __PUBLISH_EXTRACT_START__ SettingsContainer.getSettings
// Read all settings stored in a settings container.
const readDb: WorkspaceDb = container2.getEditableDb({});
const raw = readDb.getString("settingsDictionary");
const allSettings = raw ? JSON.parse(raw) as SettingsContainer : {};
// __PUBLISH_EXTRACT_END__
expect(allSettings["myApp/maxItems"]).to.equal(100);
expect(allSettings["myApp/theme"]).to.equal("dark");

editor2.close();
});

it("Find and open a workspace container by iTwinId", async () => {
IModelHost.authorizationClient = new AzuriteTest.AuthorizationClient();
AzuriteTest.userToken = AzuriteTest.service.userToken.admin;
Expand Down
Loading