The KV store
Three files already configure everything shep runs: a Flockfile configures a sheep, shep.toml configures the shepherd, and dogs.toml configures its dogs. None has a field for "the port the last on-call engineer picked" or a feature flag a provisioning script wants to leave a note about. The KV store is what's left over: a flat, file-locked junk drawer, explicitly not the primary config path.
[<name>] in dogs.toml. This store is only for what's left after those two.The three verbs
shep set <key> <value> writes a key, replacing whatever was there. shep get <key> prints one value; shep get with no key lists everything. shep unset <key> removes one key; shep unset --all empties the store: a flag, not a key named all, because nothing stops you having a key called all and shep unset all would then mean something different depending on your own store.
Real output
Run against a fresh $SHEP_HOME, no shepherd listening:
$ shep set bark.cooldown 30s KEY VALUE bark.cooldown 30s $ shep set feature.flag on $ shep get KEY VALUE bark.cooldown 30s feature.flag on $ shep get nope.key error[not_found]: `nope.key` is not set $ echo $? 3 $ shep unset bark.cooldown REMOVED 1 $ shep unset --all REMOVED 1 $ shep get KEY VALUE
A key that was never there answers NotFound on both get and unset, exit code 3 either way: a script can write shep get feature.flag || echo default and trust the exit code rather than parsing output to tell "empty" from "missing."
The key grammar
A key is [A-Za-z0-9._-], one to 128 bytes, and can't start with a dot. A dot is part of a name, not a path: bark.cooldown is one flat key, not a nested object, and shep get bark won't find it. This is deliberate: a nesting grammar here would be a second config language, with its own quoting rules, for a store that's explicitly not the primary one. The narrow alphabet is also why shep get $key never needs quoting in a script. A value is a string, capped at 4 KiB: the store is read whole on every access, and the cap is what keeps kv.json from quietly becoming a blob store.
No shepherd required
shep set/get/unset never touch the socket: they read and write $SHEP_HOME/kv.json directly, the same way shep enable writes shep.toml with nothing listening. That's what makes this store usable during provisioning, before any shepherd has ever booted on the machine. A dog reads the same store through shep_core::kv rather than over the wire: a 0600 file inside a 0700 $SHEP_HOME, opened by a process running as the same user, already has every property a socket round trip would have bought it.
Concurrent writers lose nothing
Two shep set invocations racing (two provisioning scripts, or an operator and a dog) are serialized by an exclusive advisory lock on a sibling kv.json.lock. Whichever loses the race simply waits its turn; neither write is silently dropped. The file itself is written through a BTreeMap, so keys are always in sorted order and two writes of the same content produce byte-identical files: safe to keep in a dotfiles repository, git add and diff like any other small config file. Mode is 0600.