Layout and ownership
<data-dir>/SCRIBE_FORMAT descriptor and versions (required feature auth-ownership-v1 once auth-init ran)
<data-dir>/LOCK exclusive OS lock held by serve or any maintenance command
<data-dir>/catalog.redb
<data-dir>/auth-state.redb workspace and operation ownership (only after auth-init)
<data-dir>/repos/<id>/ metadata.redb, objects/, SCRIBE_FORMAT
<log-dir>/ disposable diagnostics (daily files, bounded retention)
<runtime-dir>/ disposable: scribe-server.pid, listen, shutdown-requestAuthoritative data, logs, and runtime files are separate directories.
init refuses non-empty directories; serve refuses directories without a
recognized descriptor; no command uses the current directory implicitly. The
auth service has its own data directory with the same one-owner rule.
Routine health checks
scribe-server doctor --data-dir <data> (alias scan) is the complete
read-only check: it takes the ownership lock, so it cannot run beside the
server, examines metadata consistency and every pack, and re-hashes every
object in parallel. Schedule it before each backup and after any unclean
shutdown; --quick skips chunk hashing for a sub-second structural pass.
Exit 0 healthy, 1 problems (listed, never repaired), 2 could not run.
scribe-server usage --data-dir <data> (alias df) is the millisecond
inventory: per-repository counts and sizes, the root total, and the
filesystem's free space. It is the first look when a disk fills up and the
way to size a backup destination.
Configuration precedence
Flag, then SCRIBE_LOG (log filter) and SCRIBE_SERVER_CONFIG (config
path), then the configuration file, then defaults. Relative paths in the
file resolve against the file's directory; relative flag paths against the
current directory. Startup logs every resolved non-secret path and limit.
The full file is shown in server-usage.md.
Shutdown
SIGINT, SIGTERM, or creating <runtime-dir>/shutdown-request stops
admission and closes idle connections. Queued requests stop; started requests
get up to drain_timeout_secs for network I/O. The deadline interrupts sockets,
then the process waits for started storage jobs before releasing its root lock.
A stalled disk can extend shutdown; durable commits are never cancelled. Forced termination leaves
recoverable state: acknowledged commits are durable and the next start
truncates unreferenced pack tails and removes staging leftovers.
Security
Two deployment modes exist; choose one per data root.
Authenticated (recommended beyond one machine). The storage server serves
TLS 1.3 only, users present revocable tokens, and every operation is
authorized per repository through the shared scribe-auth service. Commit
and lock attribution comes from the token, not from a client-supplied
author. Clients pin the server's CA on first use and refuse a changed CA.
Setup, roles, token lifetimes, and revocation latency (new admissions within
30 seconds) are in auth.md. Keep CA keys offline; give each
server its own leaf certificate and its own service token.
Plaintext (loopback or tunnel only). TCP v2 without auth has no
authentication: workspace ids and author labels are self-declared, and any
peer that can reach the port can submit, lock, and unlock as anyone. The
server refuses non-loopback listen addresses unless allow_non_loopback = true and logs server.plaintext_exposed when it is. Acceptable only behind
an authenticated tunnel, for example ssh -L 7447:127.0.0.1:7447 vcs-host
on each client with listen = "tcp://127.0.0.1:7447" on the server.
Capacity and performance expectations
Recorded in tools/scribe-perf/baselines/macos-ci.json on a developer
MacBook (Apple SSD with F_FULLFSYNC), ci profile:
| item | value |
|---|---|
| durable metadata transaction (1000 adds) | p50 33 ms, p95 44 ms |
| durable lock acquire (single transaction) | 4 ms |
| accept transaction hold under 32 clients | p50 44 ms, p99 92 ms |
| publication gate wait under 32 clients (6 concurrent submitters) | p50 153 ms, p99 444 ms |
| small submit latency, idle server | p50 91 ms |
| initial import 107 MiB / 1544 files | 0.79 s |
| initial sync of the same | 0.63 s (170 MiB/s logical) |
| no-op sync | 45 ms, zero payload bytes |
| 20k small objects, packs vs loose files | 0.3 s vs 100 s |
These numbers are dominated by flush cost. Linux ext4 or xfs on a datacenter SSD flushes in 1 to 3 ms and should show roughly an order of magnitude lower transaction and gate times; that is a prediction, not a measurement.
Lock and workspace transactions share the per-repository writer with acceptance, so under load lock operations queue behind submits (lock p50 above 100 ms under load versus 4 ms idle). A separate coordination database is the planned remedy if a Linux measurement confirms it.
Disk: the server archive is roughly the logical size of unique content plus
metadata (a few hundred bytes per path revision). Each workspace costs the
logical size of its view, plus whatever accumulates in .scribe/recovery/
until the user empties it. The client cache holds only objects up to 256 KiB
plus manifests.
Performance profiles of scribe-perf
ci: about 107 MiB, 1544 files; runs in about 40 s; deterministic gates; suitable for shared CI.million-paths: 1,000,000 files of 1 to 4 KiB plus 100 medium binaries, about 2.5 GiB dataset, about 10 GiB sandbox; opt-in on dedicated hardware;--quickuses 100,000 files.scale: at least 500 GiB of generated compressible/incompressible data; requires--disk-budget-gib 1100or more; dedicated hardware only.
Baselines are machine-specific. compare refuses reports whose profile, OS,
architecture, seed, dataset size, client count, durability, or build profile
differ, and treats sub-millisecond changes below the configured noise floor
as noise. Updating a baseline is an explicit commit with an explanation.
Windows notes
Windows is the primary client platform. The client sets the read-only
attribute in --readonly workspaces, retries renames and deletes on sharing
violations and transient access-denied results from scanners, opens
directories without delete sharing, rejects reparse points, and opens long
paths with the \\?\ prefix whether or not LongPathsEnabled is set (other
tools on the machine may not handle such paths). Directory-handle flushing on
NTFS is best effort and reported as such. The Windows-specific tests run in
CI on windows-latest; read them from CI before making a Windows claim.