Documentation/Working with Scribe
Client commands8 min read

Client usage - scribe

On this page

New here? tutorial.md walks through a server, two clients, and a round trip with real output.

text
scribe [global options] <command> [options] [paths]

Every command finds its workspace by walking up from the current directory. Paths may be relative to the current directory or absolute; they must lie inside the workspace.

Global options

optionmeaning
--server URLtcp://host:port or tls://host:port. Precedence: this flag, SCRIBE_SERVER, the workspace's recorded server, tcp://127.0.0.1:7447
--token-file FILEpersonal access token for a tls:// server (or SCRIBE_TOKEN_FILE); never stored in the workspace
--trust-fingerprint BLAKE3:…approve a not-yet-trusted server only if its CA fingerprint matches; required for first use in scripts
--tls-ca FILEverify the server against this CA instead of remembered trust (or SCRIBE_TLS_CA)
--config-dir DIRper-user configuration root holding remembered trust (or SCRIBE_CONFIG_DIR)
--jsonone JSON object on stdout; diagnostics stay on stderr
--global-ignore FILElowest-precedence ignore rules
--log-level LEVEL, --log-filter FILTER, --log-format text|json, -v, -vv, -qdiagnostics on stderr; default warn; SCRIBE_LOG holds a filter

Progress is shown only when stderr is a terminal and neither --json nor -q is given. Colors follow the terminal; NO_COLOR=1 or TERM=dumb disables them, and redirected output is always plain.

Exit codes

codemeaning
0success
1user or semantic error: usage, not found, conflict, rejected submit, permission denied
2refused to destroy local data
3submit outcome unknown; the candidate is kept, run scribe submit again
4integrity failure (a digest did not match)
5transport or storage failure

Connecting to a TLS server

sh
scribe --server tls://vcs.example:7447 --token-file ~/.scribe-token repo list

The first connection to a server shows the fingerprint of its certificate authority and asks for yes. Compare it with the value your administrator gave you; the token is sent only after that trust is saved. Later connections verify against the saved CA and need no prompt. A server whose CA changed is refused until the old trust is forgotten explicitly.

sh
scribe --server tls://HOST:PORT trust add [--trust-fingerprint BLAKE3:…]   # approve without sending a token
scribe --server tls://HOST:PORT trust show                                 # offline
scribe --server tls://HOST:PORT trust forget --trust-fingerprint BLAKE3:…  # must match the saved value

Remembered trust lives outside every workspace: ~/Library/Application Support/Scribe/trust on macOS, %LOCALAPPDATA%\Scribe\trust on Windows, $XDG_CONFIG_HOME/scribe/trust (or ~/.config/scribe/trust) on Linux. Scripts and --json never prompt: pass --trust-fingerprint or approve first with trust add. A tcp:// server accepts no token and no trust options. Tokens, roles, and certificates are explained in auth.md.

Repositories

sh
scribe repo create <name>          # letters, digits, '-', '_', '.'; not starting with '.'; ≤ 128 bytes
scribe repo list                   # only repositories you may read on an authenticated server
scribe repo info <name|id>

Creating a repository on an authenticated server requires namespace administration.

Workspaces

sh
scribe workspace create <repo> <directory> [--view PREFIX]... [--author NAME] [--readonly]
scribe workspace info

The directory must not already contain a workspace. --view fixes inclusion prefixes (for example --view Assets/Characters --view Source); paths outside the view are neither synced nor addable, and changing a view means a new workspace. --author defaults to SCRIBE_AUTHOR, then the OS user name; on an authenticated server the token's user is the recorded author regardless. --readonly marks unopened tracked files read-only: edit clears the attribute; revert, sync, and submit restore it.

Opening files

sh
scribe add [--force] <path|dir>...    # new files; directories recurse with ignore rules
scribe edit <path>...                 # tracked files you will modify
scribe delete [--force] <path>...     # tracked files; removes the local file
scribe revert <path>...               # restore the recorded base; reverting an add keeps the file

add --force overrides ignore rules only: it never adds links, names Windows cannot represent, anything under .scribe/, or files outside the workspace. delete refuses a locally modified file unless --force, which moves the content to .scribe/recovery/ first. revert of a modified edit keeps the modified content there too.

Status and scanning

sh
scribe status                # opened files only (fast: size + mtime fingerprints)
scribe status --scan         # also tracked-but-unopened modifications and deletions, and untracked files
scribe status --verify       # hash every tracked file instead of trusting fingerprints
scribe scan [--verify]       # same as status --scan
scribe ignore explain <path> # the deciding rule file, line, and pattern, or the excluded parent

Fingerprints are caches; --verify is the proof. Ignore rules never hide tracked changes. status also lists unresolved merge or stale-file conflicts.

Submitting

sh
scribe submit -m "message"

A submit is the whole of "commit and push": it chunks and hashes every opened add and edit, persists the candidate with its operation id, uploads only chunks the server lacks together with manifests and the change set, and finalizes local baselines after the server's durable acknowledgement. A file that changes while it is read or uploaded is refused.

  • accepted: prints the change number, file count, chunks sent, bytes on the wire versus logical bytes, and server flush count;
  • rejected (base_mismatch, locked_by_other, path_collision, …): nothing changes locally. For stale opened files run scribe resolve --update, choose or edit each resolution, and submit again;
  • unknown (connection lost): exit 3. The candidate is kept; the next scribe submit first asks the server what happened and never creates a duplicate.

Syncing and exporting

sh
scribe sync                         # to head
scribe sync --revision <change>     # to a pinned change; an older target is a full reconcile
scribe export --revision <change> --to <empty dir> [--repo NAME] [--view PREFIX]...

Sync writes a journal before touching files, applies it in batches, reuses unchanged chunks from the local copy and the cache, verifies every reconstructed file's content id, and advances the watermark only when everything is applied. Every local file about to be replaced is moved to .scribe/recovery/ first, modified or not, and the report names each location; that directory is yours to empty. Opened paths are skipped and listed as deferred; they update on revert or on the next sync after they are closed. Files another program holds open (Windows sharing violations) are reported and retried on the next run, which resumes from the journal. export writes a pinned tree into an empty directory with no workspace state.

Branches

sh
scribe branch list
scribe branch create <name> [--from BRANCH] [--revision CHANGE]   # default: the workspace branch's head
scribe branch rename <old> <new>                                   # id, history, bindings, and locks unchanged
scribe branch retry                                                # finish a branch operation whose reply was lost
scribe switch <branch>

Branches have stable ids, so renaming changes nothing but the name. main cannot be renamed and no branch can be deleted. Creation copies metadata only and shares every asset object. switch requires no opened files, no pending submit or resolution, and no held locks; it persists its intent first and resumes after an interruption. Change numbers are one sequence per repository across all branches.

Merging and resolving

sh
scribe merge <source> [--revision CHANGE] [--base CHANGE]   # prepare a three-way merge into the workspace branch
scribe status                                               # unresolved paths
scribe resolve                                              # list them
scribe resolve --export <empty dir>                         # base/, ours/, theirs/ for an external tool
scribe resolve --ours|--theirs <path>... | --all             # pick a side
scribe resolve --working <path>...                          # accept the edited working file
scribe submit -m "Merge source"                             # the merge becomes history
scribe merge --continue                                     # resume after an interruption
scribe merge --abort                                        # give up; displaced files go to .scribe/recovery
scribe resolve --update                                     # refresh stale opened files against head, then resubmit

Non-overlapping text edits merge automatically (UTF-8, no NUL, at most 4 MiB per input). Binary, add/add, delete/edit, mode, and path-shape conflicts need a decision. ours is the workspace branch, theirs the source branch or, for stale files, the server. Merge needs a full-view workspace with no opened files. If the target branch moves before you submit, run scribe resolve --update. Limits and measured costs are in branches.md.

History, info, locks

sh
scribe log [path] [--limit N] [--before CHANGE]
scribe info <path>              # base, local state, head revision, lock holder
scribe lock <path>...           # exclusive, repository-wide across branches, held until unlock
scribe unlock [--force] <path>...
scribe locks [--prefix DIR]

A submit touching a path locked by another workspace is rejected. unlock releases only this workspace's acquisition; --force releases someone else's, and on an authenticated server needs the maintainer role.

Ignore files

.scribeignore files are versioned and nest; .scribe/ignore is local; a --global-ignore file has the lowest precedence. Git-style syntax; deeper files win; the last match in a file wins; an excluded directory is not entered. A malformed rule stops discovery rather than being skipped. Details in ignore-and-paths.md.

Typical day

sh
scribe workspace create game ~/game --view Assets --view Source --author ann
cd ~/game && scribe sync
scribe edit Assets/Hero.uasset && scribe lock Assets/Hero.uasset
# ... work ...
scribe status
scribe submit -m "hero rig update"
scribe unlock Assets/Hero.uasset
scribe sync

Compression

Repository storage and connection compression are independent. Both default to none. Choose the permanent repository storage policy at creation:

sh
scribe repo create game --storage-compression lz4
scribe --transfer-compression lz4 workspace create game ./game

The workspace creation command saves that transfer preference. Change it later from inside the workspace:

sh
scribe workspace config --compression none
scribe workspace config --compression lz4

scribe workspace info reports the saved preference. A global --transfer-compression none|lz4 overrides it for one command, for example scribe --transfer-compression none sync; it does not change the saved value. scribe repo info game reports the repository's immutable storage policy. There is no API or CLI to change that policy after creation.

LZ4 only compresses object data, leaving control messages unchanged. Objects that do not shrink remain raw on disk; transfers similarly retain raw blocks when compression does not help. Compression does not change chunk IDs, manifest IDs, deduplication, or accepted history. Working-copy files remain ordinary uncompressed files. The workspace object cache uses raw storage.

Submit and sync JSON include protocol_bytes_sent and protocol_bytes_received for that connection (handshake, metadata frames, and encoded object data; TLS and TCP overhead excluded). Existing bytes_sent/bytes_fetched workload counters count original object bytes and should not be used to measure wire compression. node tools/scenario.js compares all four storage/transfer settings on identical repetitive and random inputs, reporting submit/sync wall times, logical throughput, protocol byte counts, and physical object-file sizes.

Interactive login

sh
scribe --server tls://storage:7447 login alice --auth tls://auth:7443

The client displays Password: on stderr and hides typed input. Enter submits it; Ctrl+C cancels and restores terminal settings. The resulting session is saved and reused by later commands. Passwords are never saved in session state. Use --password-file FILE for automation; without it, redirected stdin is refused. Initial TLS trust still requires approval (or a supplied --auth-ca).

An optional scribe-gateway can provide one client endpoint for several existing storage instances. Standalone operation remains supported.

Source docs/client-usage.mdSnapshot 93d02b17