Configuration
How to configure data stores, prefixes, and access levels.
--store-dir CLI Syntax
--store-dir can be specified multiple times on any script. Each entry maps a key
prefix to a data directory.
Format
--store-dir PATH Default store (no prefix, catches all unmatched keys)
--store-dir :PATH Explicit default (colon at start, same as above)
--store-dir PREFIX:PATH Prefix-bound store (PREFIX keys route here)
Examples
# One default store
--store-dir /tmp/globals
# Default + session-specific prefix
--store-dir /tmp/globals --store-dir "session:/tmp/my-agent-session"
# Three stores: default + two prefix overrides
--store-dir /tmp/globals \
--store-dir "foo:/tmp/foo-store" \
--store-dir "foo.bar:/tmp/foobar-store"
Prefix Rules
Key prefixes must match: [a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*
- No leading, trailing, or consecutive dots
- Colons not allowed in key names or directory paths
- Each segment: letters, digits, hyphens, underscores
Validation
After all config sources are merged, there must be at least one default (empty-prefix) store. If not, the script exits with an error.
Prefix Resolution (Longest Match Wins)
When multiple prefixes overlap, the most specific one wins:
Stores: "" → /tmp/default, "foo" → /tmp/foo, "foo.bar" → /tmp/foobar
Key "foo.bar.baz" → /tmp/foobar (longest match: "foo.bar")
Key "foo.qux" → /tmp/foo (longest match: "foo")
Key "other.thing" → /tmp/default (no prefix match)
Obscuring Behavior
A prefix mount hides ALL keys under that prefix from less-specific stores, even if the mounted store doesn't have the key. This is intentional — like a filesystem mount.
/tmp/default has: foo/extra/value = "hidden"
--store-dir "foo:/tmp/foo-store" (which has no "extra" key)
get.ts --key foo.extra → NOT FOUND (obscured by the foo mount)
Config Files (YAML)
For complex setups, use a YAML config file instead of repeated CLI flags.
Format
stores:
- prefix: templates # Keys starting with "templates." (no trailing dot needed)
path: /app/data/templates
access: readonly
init:
source: definitions/templates.yaml
on_exists: fix
- prefix: session
path: /tmp/agent-session
access: write
- prefix: "" # Default catch-all (must exist)
path: /tmp/globals
access: write
init:
source: definitions/global.yaml
on_exists: fix
Fields
| Field | Type | Required | Description |
|---|---|---|---|
prefix |
string | yes | Key prefix (empty string = default). No trailing dot needed. |
path |
string | yes | Data directory path (relative paths resolved from config file location) |
access |
string | no | readonly, write (default), or admin |
init.source |
string | no | YAML definition to bootstrap from |
init.on_exists |
string | no | ignore (default), validate, or fix |
Cascading
Config files are loaded in order (later overrides earlier by prefix):
- Built-in default: single store at
/tmp/templating-vault/ - Repo-level:
.opencode/skills/templating-vault/config.yaml - User-level:
~/.config/templating-vault/config.yaml - CLI:
--config <path> - CLI:
--store-direntries
Within each level, stores are merged by prefix. If two configs define a store for the same prefix, the later one wins. Stores for other prefixes from earlier configs remain.
Agent Isolation Pattern
Different agents share globals but get their own session stores:
Config for AUTO-IMP-SUP:
stores:
- prefix: session
path: /tmp/AUTO-IMP-SUP
access: write
- prefix: ""
path: /tmp/globals
access: write
Config for AUTO-MRG-SUP:
stores:
- prefix: session
path: /tmp/AUTO-MRG-SUP
access: write
- prefix: ""
path: /tmp/globals
access: write
Both see global.credentials.forgejo_pat from /tmp/globals/ — same value.
Each sees session.max_workers from its own directory — independent values.
The same template {{ max_workers }} renders differently for each because the
config determines which data directory provides session.max_workers.