Shared repository configuration
Documentation/Workspaces and projects
Shared repository configuration10 min read

Shared repository configuration

On this page

.scribe/config.toml holds versioned project settings. It lives in Scribe's .scribe/ namespace at the repository root, where everything except .scribe/_state/ is ordinary versioned content; .scribe/_state/ remains local workspace state. It is the only shared configuration path Scribe reads, spelled exactly that way (see one spelling). .scribeignore keeps its separate, directory-scoped discovery behavior.

toml
version = 1
kind = "art"

[locks]
rules = [
  '*.uasset',
  '*.psd',
  '!Art/Generated/**',
]

[profiles.programmer]
include = ['engine', 'game']

[profiles.artist]
include = ['game', 'art-source']

[profiles.everything]
include = ['.']

[presentation]
cover = 'Marketing/key-art.psd'

[tags]
"Characters" = ["character"]
"Environments/Forest" = ["forest", "environment"]

Add, edit, and submit this file through ordinary Scribe commands. To create it by hand, run mkdir -p .scribe first, then scribe add .scribe/config.toml, or start from a preset. Its exact bytes determine its content identity; different formatting remains different versioned content even when it represents the same settings. There is no alternate file reader, automatic conversion, include directive, or environment expansion.

Format and validation

The file uses TOML, decoded by one shared parser and typed model in scribe-api::repository_config. The server and clients use the same validation.

  • version = 1 is required. Unsupported versions and unknown fields or sections are rejected, including unknown fields within lock, profile, mount, and presentation tables.
  • The entire file must be UTF-8 without NUL bytes, at most 64 KiB. Standard TOML comments, quoting, arrays, LF, and CRLF work normally. Duplicate keys or tables and values of the wrong type are errors.
  • kind, [locks], [profiles], [mounts], [presentation], and [tags] are optional. A missing file means a generic repository with no mandatory locking, no named profiles, and no repository mounts. An existing file must be valid configuration; empty text is not a valid document.
  • The server validates the complete proposed file on every branch before acceptance. Invalid configuration returns a recorded invalid_repository_config outcome; it never partially activates valid sections.

Repository kind

The optional top-level kind says what the repository holds, so clients can choose how to present it. Values are lowercase strings:

ValueMeaning
genericDefault when kind is absent. Clients use their general presentation.
artAn art and asset bank: meshes, animations, textures, audio.

Any other value, spelling ('Art'), or type is rejected. Only the accepted value on main counts; a kind on another branch has no effect until it is accepted on main. Changing the decoded value on main requires a Maintainer, like lock rules and mount topology (see section authority); writing the default kind = "generic" explicitly, or removing that line, is not a change. The kind does not affect storage, locking, or sync. In the web interface, the auto Interface preference opens art repositories in the Art interface.

Presentation

[presentation] shapes how clients show the repository, and nothing else: it does not affect storage, locking, or sync.

KeyMeaning
coverA repository path whose picture stands for the repository on its card

cover must be a valid repository path by the same rules as every other path in this file; an empty or malformed value, or any other key in the table, is rejected. Only the accepted value on main counts. Any file that gets a thumbnail can be the cover (an image, a PSD, a model's render, a video frame). While the file is not on main, or has no thumbnail yet, the web interface keeps its own choice: the newest file with a thumbnail. Like profiles, a Writer may change it.

Tags

[tags] gives files tags for finding and grouping assets, and nothing else: it does not affect storage, locking, or sync. Each key is a folder, and every file in that folder or anywhere below it gets the listed tags. A key naming a file rather than a folder gives nothing.

toml
[tags]
"Characters" = ["character"]
"Environments/Forest" = ["forest", "environment"]
"Props/Vehicles" = ["vehicle", "prop"]
  • A key is a repository folder path in its exact repository spelling, by the same rules as every other path in this file. An empty key, ., a leading or trailing /, and .. are rejected, as are two keys that are the same folder to Scribe, such as Characters and characters. A key naming a file, or a folder spelled differently from the repository (such as characters for Characters), gives no file any tags.
  • At most 256 folders, each with a list of 1 to 32 tags. An empty list, or a value that is not a list of strings, is rejected.
  • Each tag has 1 to 64 characters, is not only white space, and contains no control characters or commas. Nothing else is checked. (The web interface also bounds the lowercase spelling at 64 characters, so a tag made of the rare letters whose lowercase is longer, such as İ, can pass here and still be refused there; the configuration then shows as unreadable.)

Scribe keeps each tag exactly as written. The web interface normalizes it (lowercase, single spaces, so " Hero Ship " and hero ship are one tag) and shows these as rule tags in the asset catalog. Only the accepted value on main counts. Rules label files for every collaborator, so changing them on main requires a Maintainer, like kind and lock rules (see section authority). The comparison is of the folders and tags as written: changing character to Character is a change even though the web shows both as one tag, while reformatting, reordering folder keys, or adding comments is not. Reordering the tags within one folder's list is a change.

Lock rules

locks.rules is an ordered array of at most 1,024 strings. Missing locks or rules, and rules = [], mean no mandatory lock rules. Each pattern must be nonempty, contain no control characters, and fit Scribe's maximum path length. Comments belong in TOML outside strings, not in rule entries.

Patterns use Git-style matching with one difference: they ignore case and Unicode normalization, comparing rule and path under the same fold Scribe uses for path collisions, so *.psd also locks Hero.PSD (two paths that fold alike are one file to Scribe, and a lock must not miss either spelling). Otherwise: filename matching at any depth for patterns without /, root anchoring with a leading /, and last matching rule wins. ! makes matching paths optional; a trailing / selects the subtree. Single-quoted TOML strings preserve glob backslashes literally, for example '\#special'. Ignore traversal does not apply: a subtree rule cannot hide a later exception. See mandatory locks.

The fold is full Unicode case folding, applied to the pattern text and to the path before matching, and it can turn one character into several: ß folds to ss, fi to fi. Literal text is unaffected, so Straße/* locks STRASSE/a.psd too. The wildcards are not: ? and a […] class match one character of the folded text. Stra?e does not match Straße (folded strasse), and [ß] folds to [ss], a class matching a single s. Write such names literally, or use *.

scribe lock rules displays the active ordered rules. A path argument explains the deciding rule and its actual line in .scribe/config.toml.

Workspace profiles

Each [profiles.NAME] requires an include array with at least one nonempty string. Names contain 1–64 ASCII letters, digits, -, or _, are case-sensitive, and are limited to 128 profiles per file. Each profile allows at most 256 inclusion prefixes. Paths use exact canonical spelling and Scribe's portable path rules; . explicitly selects the entire repository.

There are no profile glob patterns, exclusions, or inheritance. Mount declarations use separate [mounts.NAME] tables; profile prefixes select their paths. Duplicate prefixes and redundant child inclusions are collapsed. The final resolved selection also has a 256-prefix limit, including local additions and the config file, which profile selections always include.

sh
scribe workspace create game ./work --profile programmer
cd work
scribe sync
scribe workspace profiles
scribe workspace view set --profile artist
scribe workspace view set --profile programmer --view art-source/Characters

Profiles are resolved from accepted history at the selected branch head, even outside the current view. Unsubmitted edits do not redefine them. A workspace saves the resolved selection and local overrides; ordinary sync does not silently apply later profile edits. Reapply the profile explicitly to adopt changes and reset overrides. See workspace views for safe transitions, interruption, and cancellation.

Section authority

SectionEffective sourcePermission on authenticated servers
kindAccepted mainMaintainer when the value changes on main
locksAccepted main, governing every branchMaintainer when the ordered rule values change on main
profilesAccepted selected branch revisionWriter to submit changes
presentationAccepted mainWriter to submit changes
tagsAccepted mainMaintainer when the folders or tags change on main
mountsAccepted selected branch revisionMaintainer when the mount topology changes on main, Writer to advance a pin; targets enforce their own access

A Writer may change profiles on main, or reformat and comment the file, while retaining the same decoded kind and ordered lock-rule strings. Quote style, table order, and source line numbers do not change the lock permission requirement. Changing rule values or their order requires a Maintainer on main. Removing nonempty rules by deleting the section, emptying the array, deleting the file, or moving it away has the same requirement. A profiles-only file with no rules can be created or removed by a Writer. Creating or removing a file with kind = "art" on main likewise requires a Maintainer, as does creating or removing one with [tags] rules.

Mount topology carries the same requirement, for the same reason: adding, removing, renaming, or retargeting a [mounts.NAME] table decides where every collaborator's nested workspace lives on disk. Advancing a mount's revision stays a Writer action, so ordinary project publishing is unaffected.

The same checks cover ordinary submits and merges. A merge into main that keeps main's .scribe/config.toml unchanged, taking none of the source branch's config edits, needs no Maintainer, whatever the source branch did to the file. Writers can submit lock-rule proposals on other branches, but those rules do not become active until a Maintainer accepts them on main. Local files, historical working copies, and profile selections cannot override active lock policy or repository permissions. The active old rules govern the submission changing the config; removing rules never releases existing locks.

Authorization compares the proposed kind and ordered rules with the exact config revision the changeset replaces. Acceptance rechecks that base under the publication gate, so a concurrent config change causes a base mismatch instead of bypassing lock permissions. Configuration reads, parsing, and rule matching happen outside the metadata write transaction. No accepted history is rewritten.

Repository mounts

Use [mounts.NAME] with path, repository, branch, and a decimal-string revision to pin an independent repository. A configuration declares at most 64 mounts; a 65th is rejected even when the file stays within the 64 KiB limit. Paths cannot overlap other mounts or parent files. A mount path cannot name a .scribe directory (in any letter case) or lie inside the root .scribe/, so .scribe, .scribe/engine, and engine/.scribe are all rejected. See versioned repository mounts for selection, child permissions, sync recovery, SDK access, and web browsing.

One spelling

The configuration path is exactly .scribe/config.toml. Scribe's path rules already refuse two tracked paths that differ only by case or Unicode normalization; in addition, the server refuses a submit on any branch that adds or changes a case or normalization variant such as .Scribe/config.toml or .scribe/Config.toml. Such a file would look like configuration and govern nothing, so it is rejected with invalid_repository_config rather than silently ignored.

Starting from a preset

Repository creation stays an administrative action (scribe repo create); there is no web page for creating repositories. To seed the configuration for an art repository, create the first workspace with a preset:

sh
scribe workspace create art-bank ./art-bank --preset art
cd art-bank
# Review .scribe/config.toml in your editor, then:
scribe submit -m "Configure art repository"

--preset art writes .scribe/config.toml into the new working copy and opens it for add. Nothing is submitted; the configuration takes effect only when you submit it, with the usual permissions (a Maintainer on main, because it sets kind and lock rules). The command is refused, before any workspace is created, when main already has an accepted .scribe/config.toml; edit that file instead. Without --profile, the --view prefixes must include .scribe/config.toml. An existing file at the path in the working copy is never replaced. The preset writes exactly:

toml
# Repository configuration; see Scribe's docs/repository-config.md.
version = 1

# Clients present an art repository as an asset bank.
kind = "art"

# Binary sources and interchange formats cannot be merged: whoever edits one
# holds its lock until they submit. Rules ignore case; the last match wins.
[locks]
rules = [
  # Painting and texturing
  "*.psd", "*.psb", "*.kra", "*.spp", "*.sbs",
  # 3D scenes and sculpts
  "*.blend", "*.ma", "*.mb", "*.max", "*.c4d", "*.hip", "*.hiplc", "*.ztl", "*.zpr",
  # Interchange
  "*.fbx", "*.abc", "*.usd", "*.usdc",
  # Engine assets
  "*.uasset", "*.umap",
]
Source docs/repository-config.mdSnapshot 2b8dcccb