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.
s3uses S3 and nothing else.SLIVINGDOC_TOKENis ignored and the credentials file is never read.hostedneedsSLIVINGDOC_TOKENor a stored login. With neither, startup is refused and says to runslivingdoc login.auto(the default) picks by what is present, in this order:- A non-empty
SLIVINGDOC_TOKENselects hosted storage. - 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.
- Otherwise S3.
- A non-empty
Two combinations are refused under auto, because they leave the intent
unclear. Both ask you to pass --storage hosted or --storage s3:
SLIVINGDOC_TOKENbeside an S3 endpoint: the--endpointflag,AWS_ENDPOINT_URL, orAWS_ENDPOINT_URL_S3.- A stored login for a space you named, beside an S3 setting. The S3
settings are the
--regionand--path-styleflags, an existing~/.aws/credentialsor~/.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 anyAWS_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--spaceorSLIVINGDOC_SPACEis 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_TOKENthe 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, thenSLIVINGDOC_SPACE, then the default thatslivingdoc 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.
--bucketand--spaceare the same setting, as areSLIVINGDOC_BUCKETandSLIVINGDOC_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, endingso set one of them. - The endpoint.
--endpoint, elseSLIVINGDOC_ENDPOINT, else the stored login’s own endpoint, elsehttps://api.slivingdoc.dev. It must behttpsunless the host is loopback. With several stored logins,--endpointorSLIVINGDOC_ENDPOINTchooses one; without it startup is refused and asks for--endpoint. A different endpoint means no stored login applies.SLIVINGDOC_TOKENnever borrows a login’s endpoint. - Not read.
AWS_REGION,AWS_ENDPOINT_URL_S3, and the AWS credential chain.--regionis accepted and unused.--path-styleis still validated, then unused. - The prefix.
--prefixstill applies, inside the space. - The token. It must be printable ASCII with no white space. A refusal
names
SLIVINGDOC_TOKENand 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 loginagain, 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_EMPTYbefore 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-rootand--private-rootmust 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.