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.
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
| Verb | What 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. |
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 setcalls 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.
| refusal | when | what to do |
|---|---|---|
config-lock-unavailable | the lock is held past ten seconds, the lock file beside the config cannot be created, or the filesystem refuses the lock | re-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-writable | the 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-mismatch | the config belongs to another user — for example sudo saphan config set … with HOME kept | re-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.