Appearance
ts storage-scope
Show and manage a storage scope — the named mount point that makes a cloud storage connection visible inside one workspace.
Usage
text
ts storage-scopes [--csv] [--no-trunc]
ts storage-scopes <workspace>
ts storage-scope <id>
ts storage-scope <id> <property>
ts storage-scope new --file=<path>
ts storage-scope set <id> <property> <value>
ts storage-scope set <id> --file=<path>
ts storage-scope delete <id> [--force]Description
A storage scope binds three things together: a workspace, a storage connection made against a storage provider, and optionally a sub-folder of that connection. Members of the workspace then see it as a named folder. Its kind says what the scope is for — 0 for user files, the locations people keep documents in, and 1 for working storage, where software puts chat uploads and tool output.
ts storage-scopes with no argument lists every scope you are allowed to see, across all workspaces. Give it a workspace — GUID or friendly id — to list just that one. The ID column holds the scope's friendly id, and both that and the scope's GUID work wherever <id> appears; the WORKSPACE column names the owning workspace. A Server Administrator, or an administrator of the scope's workspace, sees every scope; anyone else sees only the scopes in workspaces they can reach whose connection is theirs to use.
A scope is administered as a JSON document. ts storage-scope <id> prints it, new and set read it from --file, and - reads standard input.
For a single field there is a shorter path. ts storage-scope <id> <property> reads one field, and ts storage-scope set <id> <property> <value> writes one. The two write forms are complementary rather than alternatives: the accessor changes one field and leaves the rest of the document as it was, while --file replaces the whole document. All three of the fields a set applies — name, rootPath, and kind — are scalars, so the accessor covers most day-to-day changes; reach for --file when you are changing more than one of them at a time or working from a file you already have.
Property names are matched against the document's own keys with dashes and case ignored, so root-path addresses rootPath. Values are typed from the field being replaced — kind stays a number, so kind 1 writes 1 and not "1" — and you never have to know a field's wire type. A field that is currently null, such as url on a scope that is not on a file system provider, is the exception: there is nothing to infer a type from, so the value goes in as a string.
A structured field has no accessor form. connection is an object, so it is refused with a pointer at --file — which will not help either, since a scope cannot be re-pointed at another connection at all. A property the document does not carry is refused too. Both refusals happen before anything is written, so a typo cannot half-apply.
The accessor is a read-modify-write: it fetches the document, changes the one field, and sends the whole document back. That is safe here because the document round-trips untyped, so a field this version of ts does not know about is preserved rather than dropped. It then reads the field back and fails if the server did not keep the value, so naming a field the server discards on a write fails rather than reporting success.
set applies three fields and ignores the rest. Only name, rootPath, and kind are read from the document; workspaceId, cloudStorageAccessTokenId, and friendlyId are accepted and discarded. A scope cannot be re-pointed at a different workspace or a different connection — delete it and create a replacement.
A printed document can seed a create. Its workspaceId is the workspace's GUID and its cloudStorageAccessTokenId the connection's, which is what new takes; new also accepts the workspace's friendly id. Give the copy its own name, which must be unique within the workspace, and its own friendlyId, or leave friendlyId out to have one generated from the name. A create needs workspaceId, cloudStorageAccessTokenId, and name; friendlyId, rootPath, and kind are optional.
A read without Server Administrator membership hides the root path and the connection. Such a read blanks rootPath, and cloudStorageAccessTokenId and connection.rowGuid are left empty. A Server Administrator's own sign-in reads them in full, so --local is not required for a round trip.
Who Can Write
Creating, changing, and deleting a scope needs an administrator of its workspace, and mounting also depends on the connection:
- A global connection is mounted, and its scopes changed, only by a Server Administrator. Administering a workspace is not authority over the server's storage.
- A per-user connection carries one person's grant, so it is mounted only by its owner, and only into the owner's personal workspace.
A connection you cannot see is reported as not found, so a mount attempt does not confirm that it exists.
Creating a Scope
The name must be unique within the workspace, and the friendly id — generated by slugifying the name when you omit it — must be unique across every scope on the server. The connection's provider must not have been deleted. A rootPath that points at a sub-folder is refused for providers that can only be mounted at their root, because the boundary could not be enforced later.
Working storage is kept apart from everything else. A connection that holds a working-storage scope holds no scope of another kind, working-storage scopes of different workspaces keep to folders of their own, and no other connection or scope may reach a working-storage folder. A working-storage scope also keeps its kind and its folder once made, since its files would stay behind. A create or change that breaks one of these rules is refused with the reason.
Deleting a Scope
ts storage-scope delete prompts unless you pass --force, and it is a soft delete. It is refused outright while a workspace has the scope set as its chat attachment storage; change that setting first. Everything else that reads a scope filters deleted rows out, so the mount disappears for workspace members as soon as the command returns. A deleted working-storage scope still holds its folder, because its files outlive the record, until an administrator releases it once the folder is empty, from the Scopes tab of Workspaces → Storage in the Administration site.
Options
| Flag | Description | Default/Values | Notes |
|---|---|---|---|
--file | JSON document to send. | PATH, or - for standard input | Required by new. set needs it only for the whole-document form. |
--force, -f | Delete without confirming. | delete only. Also required when running non-interactively. | |
--csv | Tab-separated output, nothing truncated. | ts storage-scopes only. | |
--no-trunc | Do not truncate columns. | ts storage-scopes only. |
Output
print writes the document and nothing else, so it pipes cleanly:
json
{
"id": "6b0d5a41-2c8e-4f13-9a77-0e5b3d1c8a92",
"workspaceId": "e2a7c4d1-93b0-4f6e-8d15-7a3c9b2e0f48",
"workspaceFriendlyId": "engineering",
"workspaceName": "Engineering",
"cloudStorageAccessTokenId": "a41e7d90-55b2-4c08-8f3e-6d2a9b40c157",
"name": "Team Files",
"friendlyId": "team-files",
"rootPath": "Projects/2026",
"kind": 0,
"url": null,
"created": "2026-05-04T18:22:31.44Z",
"deleted": null,
"connection": {
"rowGuid": "a41e7d90-55b2-4c08-8f3e-6d2a9b40c157",
"name": "HQ OneDrive",
"storageProvider": {
"slug": "onedrive",
"name": "OneDrive",
"storageType": 0,
"connectionMode": 1
}
},
"unservedReason": null
}url is populated only for scopes on a file system provider, where it is the served path the scope is reachable at. unservedReason says why a scope is not served — it shares a connection or a folder with working storage, an arrangement made before that was refused — and is null for a scope that is. Under --format=json the document is wrapped in the standard result envelope as result.entity, which is not what set consumes. Use the default text output for round trips.
A single-field read prints the bare value — Projects/2026, not "Projects/2026" — so it drops straight into a shell variable, which makes it the quickest way to confirm where a scope is mounted. Under --format=json that value comes back in the envelope under the document's own key, result.rootPath.
Examples
bash
# Every scope you can see, and which workspace each belongs to
ts storage-scopes
# Just one workspace's scopes
ts storage-scopes engineering
# Full names, untruncated
ts storage-scopes --no-trunc
# Where is one scope mounted?
ts storage-scope team-files root-path
# Rename it, or move it to another sub-folder — one field, no file
ts storage-scope set team-files name "Team Files (2026)"
ts storage-scope set team-files root-path Projects/2027
# Several fields at once: the document round trip
ts storage-scope team-files > scope.json
$EDITOR scope.json
ts storage-scope set team-files --file=scope.json
# Mount a global connection into a workspace
cat <<'JSON' | ts storage-scope new --file=-
{ "workspaceId": "engineering", "cloudStorageAccessTokenId": "a41e7d90-55b2-4c08-8f3e-6d2a9b40c157",
"name": "Team Files", "rootPath": "Projects/2026", "kind": 0 }
JSON
# Unmount a scope
ts storage-scope delete team-files --forcebash
# Without Server Administrator membership, a read blanks rootPath, and the
# accessor writes the whole document back. Changing your personal scope's name
# this way also clears its rootPath, mounting the connection's root. Send only
# the field you mean instead.
echo '{"name":"Team Files (2026)"}' | ts storage-scope set team-files --file=-Exit Codes
0 on success. -1 when the server rejects the document — a missing workspaceId, cloudStorageAccessTokenId, or name, a duplicate name or friendly id, an unrecognized kind, a provider-less or deleted-provider connection, another person's connection, a sub-folder path on a provider that must be mounted at its root, an arrangement that breaks the working-storage rules — when an accessor write does not read back as the value sent, when a delete is blocked because a workspace uses the scope for chat attachments, when new is run without --file, when set is run with neither --file nor a property and value, or when a delete is declined or run non-interactively without --force. -2 when the property holds an object or array, which is how connection comes back, and when set is given both a property and --file. -4 when no account is signed in. -5 when the account is a member of the scope's workspace but not one of its administrators, when it mounts or changes a scope on a global connection without being a Server Administrator, and when it mounts its own per-user connection anywhere but its personal workspace. -6 when no scope has that id, when a property, read or written, is not a field of the document, when the --file path does not exist, when the workspace or connection named in a create does not exist or cannot be seen, and when the scope exists but is in a workspace you cannot see or are not a member of — visibility failures are reported as absence on purpose.
Related Commands
- ts storage-provider — The backend a scope's connection is made against; register it first.
- ts storage-connection — The connections a scope mounts.
- ts workspace — The workspace a scope belongs to, and the source of the workspace argument.
- ts share — The other way content leaves a workspace.
