Configuration

Every flag, environment variable, and default, with precedence rules, how the storage backend is chosen, and the session-directory and shared-cache notes.

serve, pull, and commit read the same flags and environment variables. Flags override environment variables, and the environment overrides defaults. Which backend a process uses, S3 or the hosted service, follows Choosing the storage. In S3 mode a bucket is required, as --bucket or SLIVINGDOC_BUCKET; in hosted mode it is optional. --space and SLIVINGDOC_SPACE are the hosted names of the same setting. -h on any of the three prints its own help followed by this reference.

Function Flag Environment variable Default
Storage backend --storage SLIVINGDOC_STORAGE auto (or hosted, s3)
S3 bucket --bucket SLIVINGDOC_BUCKET S3: required; none otherwise
Hosted space --space SLIVINGDOC_SPACE the token’s space, else the login’s default space
Object prefix --prefix SLIVINGDOC_PREFIX slivingdoc
S3 region --region AWS_REGION us-east-1
S3 endpoint --endpoint AWS_ENDPOINT_URL_S3 empty (AWS resolution)
Hosted API endpoint --endpoint SLIVINGDOC_ENDPOINT https://api.slivingdoc.dev, or the endpoint of the stored login
Hosted API token none SLIVINGDOC_TOKEN empty (a stored login, else S3)
Credentials directory none SLIVINGDOC_CONFIG_DIR <user-config-dir>/slivingdoc
S3 path-style access --path-style SLIVINGDOC_PATH_STYLE false
Workspace root --workspace-root SLIVINGDOC_WORKSPACE_ROOT session dir for serve, working dir for pull and commit
Private state root --private-root SLIVINGDOC_PRIVATE_ROOT session dir, else user cache
CAS retry limit --commit-retries SLIVINGDOC_COMMIT_RETRIES 8 (0..100)
Checkpoint pack count --checkpoint-packs SLIVINGDOC_CHECKPOINT_PACKS 256 (minimum 1)
Retained checkpoints --retained-checkpoints SLIVINGDOC_RETAINED_CHECKPOINTS 1 (0..64)
Read-only paths --read-only-paths SLIVINGDOC_READ_ONLY_PATHS empty (no read-only path)
Writable paths --writable-paths SLIVINGDOC_WRITABLE_PATHS empty (no confinement)
Log levels --log-level LOG_LEVEL info
Log timestamps --log-timestamp SLIVINGDOC_LOG_TIMESTAMP true

--workspace-root is the root below which request paths may live, and is also the notebook directory an omitted path resolves to. The private root holds the internal Git repository, the state record, and the operation locks. It must not be at or below the workspace root. Both roots become absolute before startup.

login and logout read SLIVINGDOC_SITE (or --site) to name the site that issued a login; the default is https://www.slivingdoc.dev. See CLI reference.

Choosing the storage

--storage (or SLIVINGDOC_STORAGE) is auto, hosted, or s3, and any other value refuses startup. Every startup decides the backend again.

  • s3 uses S3 and nothing else. SLIVINGDOC_TOKEN is ignored and the credentials file is never read.
  • hosted needs SLIVINGDOC_TOKEN or a stored login. With neither, startup is refused and says to run slivingdoc login.
  • auto (the default) picks by what is present, in this order:
    1. A non-empty SLIVINGDOC_TOKEN selects hosted storage.
    2. Otherwise a usable stored login selects hosted storage when the space is the login’s stored default, or when nothing on the machine configures S3.
    3. Otherwise S3.

Two combinations are refused under auto, because they leave the intent unclear. Both ask you to pass --storage hosted or --storage s3:

  • SLIVINGDOC_TOKEN beside an S3 endpoint: the --endpoint flag, AWS_ENDPOINT_URL, or AWS_ENDPOINT_URL_S3.
  • A stored login for a space you named, beside an S3 setting. The S3 settings are the --region and --path-style flags, an existing ~/.aws/credentials or ~/.aws/config, and any of these variables, set and non-empty: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_PROFILE, AWS_REGION, AWS_DEFAULT_REGION, AWS_CONFIG_FILE, AWS_SHARED_CREDENTIALS_FILE, AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ENDPOINT_URL, AWS_ENDPOINT_URL_S3, SLIVINGDOC_PATH_STYLE, and any AWS_CONTAINER_CREDENTIALS_*. Other AWS variables do not count. A space you did not name, taken from the login’s stored default, is not refused, because S3 would have no bucket to use. Naming that same space with --space or SLIVINGDOC_SPACE is refused.

Every startup refusal is one line on stderr that begins error: app: . Configuration refusals continue invalid configuration: , as below; the sections that follow show each other refusal from app: on.

error: app: invalid configuration: SLIVINGDOC_TOKEN and an S3 endpoint (--endpoint) are both configured; pass --storage hosted to use hosted storage (the token goes only to the hosted endpoint), or --storage s3
error: app: invalid configuration: a stored login and S3 settings (AWS_PROFILE) are both configured for "notes"; pass --storage hosted or --storage s3

A region, AWS credentials, or a profile beside SLIVINGDOC_TOKEN are not a refusal: hosted mode reads none of them. SLIVINGDOC_ENDPOINT names the hosted API and is never an S3 setting.

The token has no flag, so it never appears in a process listing. It wins over a stored login.

Hosted mode

Hosted mode stores the notebook through the hosted storage API at slivingdoc.dev instead of S3, using either SLIVINGDOC_TOKEN or a login made with slivingdoc login. It needs slivingdoc 0.2.0 or newer for the token and 0.2.4 or newer for login. A 0.1.x release ignores the token and stays in S3 mode. What changes:

  • The space. A token reaches exactly one space, so with SLIVINGDOC_TOKEN the space can be left out: slivingdoc asks the API which space the token reaches (0.2.2 and newer; 0.2.0 and 0.2.1 need --bucket). With a stored login the space is --space, then SLIVINGDOC_SPACE, then the default that slivingdoc space <name> stored; a token process never uses the stored default. A space you name must be 1 to 63 lowercase letters, digits, and inner hyphens. With a token it must also be the token’s own space.
  • One setting, two names. --bucket and --space are the same setting, as are SLIVINGDOC_BUCKET and SLIVINGDOC_SPACE. Giving both spellings of one layer the same value is fine. Different values refuse startup: error: app: invalid configuration: --bucket "a" and --space "b" name different spaces; they are one setting, so pass one of them, and the same for the two variables, ending so set one of them.
  • The endpoint. --endpoint, else SLIVINGDOC_ENDPOINT, else the stored login’s own endpoint, else https://api.slivingdoc.dev. It must be https unless the host is loopback. With several stored logins, --endpoint or SLIVINGDOC_ENDPOINT chooses one; without it startup is refused and asks for --endpoint. A different endpoint means no stored login applies. SLIVINGDOC_TOKEN never borrows a login’s endpoint.
  • Not read. AWS_REGION, AWS_ENDPOINT_URL_S3, and the AWS credential chain. --region is accepted and unused. --path-style is still validated, then unused.
  • The prefix. --prefix still applies, inside the space.
  • The token. It must be printable ASCII with no white space. A refusal names SLIVINGDOC_TOKEN and never prints its value.

Stored login

slivingdoc login stores an account key in credentials.json in SLIVINGDOC_CONFIG_DIR, by default <user-config-dir>/slivingdoc. The key never reaches storage. serve, pull, and commit trade it at the site for a token of one space that lasts an hour, keep the token in memory only, and renew it. The file is read only when the token variable is unset and --storage is not s3. slivingdoc refuses a file that is a symbolic link or not a regular file, that another user can access (any group or other permission bit), that another user owns, that sits in a directory others can write to, that is larger than 1 MiB or malformed, or that has another format version, and says how to fix it or to pass --storage s3. SLIVINGDOC_CONFIG_DIR must be an absolute path. A relative value is not an error: serve, pull, and commit behave as if no login were stored, and print nothing about it. Startup is also refused, with a message that names the fix, when:

  • the login has expired: run slivingdoc login again, or pass --storage s3;
  • there is no space to use, and the message names slivingdoc space;
  • the site refuses to mint a token for the space.
error: app: invalid configuration: the stored login for https://api.slivingdoc.dev has no default space; run 'slivingdoc space' to list its spaces and 'slivingdoc space <name>' to choose one, or pass --space (for S3, pass --storage s3 and --bucket)
error: app: the stored login could not mint a token for space "notes" at https://www.slivingdoc.dev: ...

Startup check

At startup, instead of the S3 compatibility probe, slivingdoc checks that the API answers and that the token was granted the space. The check only reads, so a read-only token passes it; its commits are then refused. A refused token stops startup, and the hint names what to check:

error: app: hosted storage refused the token: ...; check SLIVINGDOC_TOKEN and --space
error: app: hosted storage refused the token: ...; check SLIVINGDOC_TOKEN: it named space "notes" but was then refused
error: app: hosted storage refused the token minted for space "notes": ...; run 'slivingdoc space' to list the login's spaces, or 'slivingdoc login' again

The first form names the spelling you used (--space, --bucket, SLIVINGDOC_SPACE, or SLIVINGDOC_BUCKET) and applies to a token from the variable. The second applies to a token that named its own space and was then refused. The third applies to a token minted from a stored login.

A token that reaches another space than the one you named is refused before the check:

error: app: the token reaches hosted space "notes", not "other" from --space; drop --space to use the token's space, or use a token made for "other"

For an environment variable the message says unset instead of drop. On a server that cannot say which space a token reaches, a space you name is kept. With none, startup says error: app: hosted storage cannot name the token's space; pass the space name as --space or SLIVINGDOC_SPACE.

When a commit would take the account past its storage limit it is refused as STORAGE_FULL: nothing is published, your edited files stay in place, and pulls keep working. slivingdoc first tries to compact the space into one checkpoint, which lets a commit that deletes notes shrink a full space. Past the monthly request allowance, writes are refused as REQUEST_LIMIT and reads slow down, then return RATE_LIMITED. Limits and prices are on Pricing.

Note: From 0.2.5 the private state of a hosted directory is keyed by the space’s own id, not only its name. After upgrading, the first pull of an existing hosted directory is a first pull: a directory whose files equal the space pulls cleanly, and one with an uncommitted edit or a file the space lacks is refused as DIRECTORY_NOT_EMPTY before anything changes. Move the files aside, pull, copy the edits back, and commit.

How values are read

Flags override environment variables, which override defaults — with one sharp edge: an explicitly empty flag value does not fall back to the environment. --read-only-paths= clears an inherited SLIVINGDOC_READ_ONLY_PATHS rather than falling back to it. That is how a process asks not to inherit a setting its parent exported. It works for the string flags that accept an empty value: --bucket, --space, --prefix, --endpoint, --read-only-paths, and --writable-paths (--region too, in hosted mode). The other flags refuse startup on an empty value: --workspace-root and --private-root (must not be empty), --commit-retries, --checkpoint-packs and --retained-checkpoints (invalid integer), --path-style and --log-timestamp (invalid boolean), --storage (not one of auto, hosted, s3), and --region in S3 mode (region is required).

Boolean values are parsed as Go booleans, so true, false, 1, and 0 all work. Decimal integer values do not accept a sign. An invalid flag value refuses startup before any native or network dependency is touched; the one exception is LOG_LEVEL, whose malformed value is reported and falls back to info — see Logging and profiling.

The session directory

serve with neither root configured takes a per-process session directory and puts both roots inside it:

<tmp>/slivingdoc-<random>/notebook    the workspace root
<tmp>/slivingdoc-<random>/private     the private root

This is the default because it needs no configuration and no coordination: every server gets its own notebook directory and its own private state, so concurrent agents never contend for one operation lock. The tools then need no path, and both the server instructions and every tool result name the directory. The whole session directory is removed at shutdown — the durable notebook is the bucket, so nothing of value is in it. A process killed outright leaves its directory for the operating system to reap; no later process reuses it, because the derived private key binds to that random path.

Configuring either root turns the default off, and neither root is removed at shutdown. Use that when humans and agents share one directory, or when you want the notebook to survive a server restart on disk. pull and commit never take a session directory: they default to the working directory, which you can still open after the process exits.

The shared pack cache

Downloaded pack bytes are cached in one durable directory per notebook, shared by every workspace and every process on the machine. It is always on, and there is no flag: --shared-pack-cache and SLIVINGDOC_SHARED_PACK_CACHE from 0.1.x are gone, and passing the flag is an unknown-flag error.

<user-cache-dir>/slivingdoc/pack-cache/<bucket>-<prefix>-<digest>/

Every server addressing the same endpoint, region, bucket, and prefix (and, in hosted mode, the same space id) computes the same directory from its own configuration, so agents share downloads with no coordination: the first cold pull populates the directory and later pulls by any agent read from it. Entries are keyed by SHA-256 and re-verified against the authoritative manifest on every read, so a corrupt or foreign entry is discarded and re-downloaded, never trusted. Only pack bytes and the store compatibility proof are shared: each workspace keeps its own private repository, baseline, and locks.

The same directory records the outcome of the S3 startup probe in probe-ok.json. A process reuses a record written by the same slivingdoc version for the same endpoint, region, bucket, prefix, and addressing mode within the last 24 hours and starts without probing. An absent, corrupt, foreign, or expired record means the probe runs and rewrites it. The record does not bind credentials, so a credential that stopped working is refused by the first request, not at startup. Delete the file to force a probe on the next start. Hosted mode runs its access check instead and records no proof.

Two cases fall back to a private cache inside each workspace’s private state: no resolvable user cache directory (no HOME and no XDG_CACHE_HOME), and a workspace root that contains the user cache directory, where the shared directory would become notebook content.

The directory names make manual cleanup easy. Nothing prunes the directory. Remove a notebook’s directory when you are done with it, and the next pull simply re-downloads.

Note: Writing into the shared cache is best-effort. A read-only or full cache directory logs a warning and the operation continues, which is what makes a pre-populated read-only cache, baked into a container image for example, work as-is.

Warning: --workspace-root and --private-root must never point at the same directory, and the private root must not sit at or below the workspace root. Startup refuses otherwise.

See S3 requirements for the bucket permissions this configuration assumes, and Restrict agents with path policies for --read-only-paths and --writable-paths in practice.

Last updated September 29, 2026

Type to search the documentation.