config.yaml
The configuration is one folder: ~/.config/sandcastle, or $SANDCASTLE_CONFIG, or the folder
given with --config DIR.
config.yaml the general settings and the toolbox
sources/<name>.yaml one file per source
It holds no secret, so it can be kept in version control. examples/config.yaml and
examples/sources/notes.yaml are a complete configuration to start from.
Every problem is reported at once, by check, install and the core when it starts, with the
file and the key. A key the program does not know is an error, so a typo is never ignored. A
folder with no file in sources/ is an error.
General settings
The keys at the top of config.yaml.
| Key | Default | What it sets |
|---|---|---|
data_dir | ~/.local/share/sandcastle/jobs | where each job’s record is kept; see Files and folders. A folder that holds the secrets folder, or lies inside it, is refused |
retention_days | 30 | records older than this many days are deleted; at least 1 |
capabilities | none | one block per capability of the toolbox, claude and ocr; see below |
Destinations
Each source, and the claude capability, has a destination block: the one place it may reach,
and the secret sent there.
destination:
address: https://notes.example.com/api/worker
secret: notes
| Key | Default | What it sets |
|---|---|---|
destination.address | required | https:// and a full domain name, with an optional port (443 for https://, 80 for http://) and path. No IP address, wildcard, range, user, password, query or fragment. http:// only with internal |
destination.secret | required | the name of a secret |
destination.internal | none | one ip:port inside your network that the address’s name stands for; see Reach a service inside your network |
The gate’s list of names is made from the destinations: nothing else can be reached.
Sources
One file per source, in sources/. Every key is required.
| Key | Default | What it sets |
|---|---|---|
name | required | the source’s name, and its destination’s. No slashes, dots or spaces, not claude or ocr, and not another source’s |
destination | required | where the source is, and its secret; see Destinations |
poll_interval | required | how often the source is asked for jobs; at least 1s |
capabilities | required | the capabilities the source may use, each enabled in config.yaml; a job for another fails |
max_jobs_per_hour | required | how many jobs of this source run in an hour, at most; at least 1 |
job_timeout | required | how long one job may run, its box included; at least 1s. Give it a little more than the capability’s timeout |
claude
capabilities.claude in config.yaml. What a job sends and gets back is in
claude.
| Key | Default | What it sets |
|---|---|---|
enabled | false | whether sources may use it |
destination | required | the Claude API, written by hand: https://api.anthropic.com, with the key’s secret |
model | required | the model every job asks, such as claude-haiku-5-5 |
effort | low | how hard the model works: low, medium, high, xhigh or max |
max_tokens | 16000 | the longest reply; a reply cut off here fails the job |
system_prompt | a fixed, generic one | the system prompt of every request |
timeout | 5m | how long the job’s box may run, the request included |
cooldown | 15m | how long the capability rests after a failure that is not the job’s |
max_input | 256KiB | the largest job input, and the largest prompt sent |
max_output | 1MiB | the largest line the box may write back |
box.memory | 256MiB | the box’s memory, with no swap on top |
box.cpus | 1 | the box’s processors |
box.pids | 32 | the box’s processes |
box.tmp | 16MiB | the size of /tmp, the box’s only writable folder |
ocr
capabilities.ocr in config.yaml. What a job sends and gets back is in ocr.
It reaches nothing outside its box, so a destination is refused.
| Key | Default | What it sets |
|---|---|---|
enabled | false | whether sources may use it |
languages | por+eng | the Tesseract languages, joined with +; only these go into the image |
max_pixels | 25000000 | the largest image decoded; a larger one fails the job |
max_edge | 2500 | the longest side an image is scaled down to before it is read |
timeout | 30s | how long the job’s box may run |
cooldown | 15m | how long the capability rests after a failure that is not the job’s |
max_input | 16MiB | the largest job input; a larger one fails before a box starts |
max_output | 1MiB | the largest line the box may write back |
box.memory | 1GiB | the box’s memory, with no swap on top |
box.cpus | 1 | the box’s processors |
box.pids | 64 | the box’s processes |
box.tmp | 64MiB | the size of /tmp, the box’s only writable folder |
The box block of each capability holds its four limits. A limit above its default is allowed,
and check warns of it: what the box then spends is outside the promise. No key opens a lock of
the box; see How a job is boxed.
Values
- A duration is a number and a unit:
30s,15m,1h30m. - A size is a number of bytes, plain or with
KiB,MiBorGiB:1024,256KiB,16MiB. box.cpusis a number above 0, such as1or0.5.box.pidsis a whole number of at least 1.
Keys that are gone
A file that still has one of these is refused, with what replaces it.
| Key | What replaces it |
|---|---|
sandbox | nothing: every job runs in a box of its own |
tesseract | nothing: Tesseract comes in the ocr box’s image |
api_key_file | sandcastle secret set claude, named in destination.secret |
base_url | destination.address |
token_file | sandcastle secret set <name>, named in destination.secret |