Saphan StudioDocs
Configuration

Configuration

Where the operator's configuration file lives, its format, and the three verbs that write it.

For the operator who has to decide the values this product will not guess.

After reading it you can find the file, read its format, set a key without disturbing the others, and see which tier produced an effective value.

Before you start: an installed machine. Identity is minted here, so this is the first section after an install.

Read in this order. This page first — the file, its format and the three verbs that write it. Then the page for whichever decision you are making; the pages after it are independent of one another.

Location and format

One file per operator, not per workspace: <prefix>/etc/saphan.conf. On a per-user install that is ~/.saphan/etc/saphan.conf; on a machine-wide install it is /opt/saphan/etc/saphan.conf. An older installation may still carry the file at its earlier spelling (~/.saphan/config); it is read there when it is the only file present, and saphan install is the act that moves it — nothing else relocates an operator's file.

The config set|show|init verbs never resolve a workspace, unlike gate/merge/run/teardown; a --workspace flag beside them is accepted and ignored. config set has no --config flag and there is no environment override for the file's location. The file is written mode 0600.

Format: bracketed sections, key = value, with blank lines and # comments ignored. Values may be quoted or bare — one layer of surrounding double quotes is stripped on read.

[identity]
actor = "alice"
display_name = "Alice Example"
email = "[email protected]"

[workspace]
default = "/path/to/your/workspace"

[credentials]
file = "/home/alice/.saphan/etc/credentials.json"

[lane.local]
base_url = "http://llm.example.internal/v1"
credential = "LITELLM_API_KEY"
api_protocol = "openai-chat"

An absent file is simply the empty configuration — no error. A present but malformed file is a hard error: a line without = is a named load failure (malformed line … (want key = value)), never a silent skip.

The three verbs

VerbWhat it does
saphan config set <key> <value>Writes one dotted key, preserving every other key already in the file — the same round-trip discipline git config gives its own file. Unknown keys and invalid policy values are refused with the full accepted vocabulary in the error.
saphan config show [--provenance] [--json]Prints effective values; --provenance names which tier produced each one.
saphan config init [--from git|system] [--actor …] [--display-name …] [--email …]Mints identity: proposes from git or the system account, but identity.actor is always human-confirmed. With no TTY and no --from/--actor it refuses by name and writes nothing — it never guesses an identity.
saphan config set identity.actor alice
saphan config set identity.display_name "Alice Example"
saphan config show --provenance
saphan config init --from git

A fourth family, saphan config forge, shows and migrates forge connections; see saphan config.

What config set guarantees

  • One lock across the whole read–modify–write. A second writer waits up to ten seconds and then refuses; two concurrent config set calls cannot lose each other's key.
  • An atomic write. The new bytes go to a temporary file beside the config and are renamed over it, so a reader sees the old file or the whole new one, never a truncated one.
  • A refused act writes nothing — the config file stays byte-identical.
refusalwhenwhat to do
config-lock-unavailablethe lock is held past ten seconds, the lock file beside the config cannot be created, or the filesystem refuses the lockre-run; if it repeats, find what holds <config>.lock, make the directory writable, or keep the config on a local filesystem. There is no unlocked fallback
config-not-writablethe config exists and this user cannot open it for writing (for example a deliberate chmod 0400)make it writable if the freeze is no longer wanted
config-owner-mismatchthe config belongs to another user — for example sudo saphan config set … with HOME keptre-run as the owning user

⚠ A sudo write that is refused can leave a root-owned <config>.lock behind, because the lock file is created before the owner check. The owning user's next write then refuses config-lock-unavailable. Remove or chown that one lock file deliberately. Otherwise never delete the lock file: the lock is released by the kernel when its holder exits, and deleting the file lets a second writer lock a different file. A hand edit in an editor takes no lock at all. ⚠ The lock and the atomic rename have been exercised on Linux only; macOS and Windows are not proven.

What this section does not cover

⚠ The complete key reference is saphan config set --help, generated from the same list the binary accepts. These pages carry the keys an operator decides, with the vocabulary each one accepts. ⚠ A credential's VALUE is never a configuration key — every secret reaches the product as the NAME of an entry in the one credentials file (or, for a few server keys, the NAME of an environment variable); Secrets says which. ⚠ Permission classes and egress policies are configured but not explained here: their reasoning is Security and Egress control.

On this page