On this page
Credential boundary
Skryba owns detector API credentials. For model inference it uses either a Brama client token resolved by role or a bearer read from an explicitly selected owner-only file for an OpenAI-compatible service at the configured URL. The two routes are mutually exclusive; candidate generation and quality judging use the selected route.
Credential domains
| Domain | Variables | Used by |
|---|---|---|
| Detectors | GPTZERO_API_KEY, WINSTON_API_KEY, ORIGINALITY_API_KEY, SAPLING_API_KEY |
score, rewrite, train |
| Brama | BRAMA_TOKEN_ROLE; optional paired SKRYBA_AGENT_ID and SKRYBA_AGENT_AUTH_SECRET_ROLE |
rewrite, train, benchmark |
| OpenAI-compatible service | SKRYBA_MODEL_SERVICE_KEY_FILE naming an owner-only bearer file |
rewrite, train, benchmark, folder prose selection and samples generation |
score does not need Brama. benchmark does not call detectors. Skryba holds no marketplace credential at all: paying a person to rewrite text runs on the HandToHuman marketplace ledger through the handtohuman market CLI, which owns that boundary and its money.
Detector selection
Without --detectors, Skryba includes every detector whose key exists and is non-empty. With --detectors, the list is trimmed and lowercased, and each named provider's key must exist. This means environment changes can change the ensemble; pin names when scores must be comparable.
Detector requests do not follow HTTP redirects. A redirected scoring endpoint is refused with the provider's status and body, without forwarding API-key headers or submitted text to the redirect destination.
For a selected detector, blank and non-Unicode key or endpoint declarations are refused by name. Only an absent endpoint override selects the provider's documented endpoint. Credential header parsing failures identify the detector and setting without exposing the key; parsed API-key and authorization headers are marked sensitive so their Debug representation does not disclose it.
Exact refusals:
no detector credentials found; configure GPTZERO_API_KEY, WINSTON_API_KEY, ORIGINALITY_API_KEY, or SAPLING_API_KEY<KEY_VARIABLE> is required for <detector>unknown detector "<name>"; available detectors: gptzero, originality, sapling, winston
Model-route authentication
BRAMA_TOKEN_ROLE names ROLE#FIELD, not a vault item or a secret value.
Skryba resolves it with stado credentials get --role ROLE --field FIELD
and sends the returned value as the bearer. STADO_BIN selects the executable.
The optional agent identity is all-or-nothing:
SKRYBA_AGENT_ID and SKRYBA_AGENT_AUTH_SECRET_ROLE must be configured together
The agent secret is resolved through the same role interface. When both are
present, src/app/brama/mod.rs signs each request in addition to sending the bearer.
The retired BRAMA_TOKEN and SKRYBA_AGENT_AUTH_SECRET environment values are
not consumed.
For a service without Brama, pair --model-service-url (or
SKRYBA_MODEL_SERVICE_URL) with SKRYBA_MODEL_SERVICE_KEY_FILE, a regular
owner-only file whose non-empty text is the service bearer. The key-file route
does not sign the /v1/chat/completions request. The file is opened and
inspected before reading content; unreadable, nonregular, differently owned,
group/other-readable and empty files are refused with the path and cause.
The selected URL determines its credential: --brama-url requires
BRAMA_TOKEN_ROLE and refuses a service key-file declaration;
--model-service-url requires the key file and refuses Brama token or
signing declarations. A crossed route refuses before any bearer is sent.
The URL flags conflict. There is no fallback between routes after a
failed request.
Inference does not follow HTTP redirects. The redirect's status and response body are reported instead of forwarding request content or agent headers to another destination. Configure the final endpoint of the selected model route.
The CLI writes wait start/end/failure lines to stderr for the model request
and response body. A connection failure names the endpoint and its transport
cause; a non-success response names its HTTP status and body. A successful
response without a string choices[0].finish_reason or
choices[0].message.content is refused with the missing field and response
text, rather than interpreted as an empty answer. A model-imposed output
limit is refused as a truncated response.
Credential role references, SKRYBA_AGENT_ID and STADO_BIN distinguish absent
settings from present-but-blank or non-Unicode values on the Brama route.
The direct route checks that Brama token and signing declarations are absent
without resolving those credentials. Only an absent STADO_BIN selects stado.
An explicitly empty SKRYBA_CREDENTIALS_FILE is refused rather than selecting
Stado; native non-Unicode filesystem paths remain valid paths.
Without Stado, explicitly select SKRYBA_CREDENTIALS_FILE: an owner-only JSON
regular file shaped as {"role":{"field":"secret value"}}. It answers the same
role references instead of calling Stado; it is not tried after a Stado failure.
Permission inspection and content reading use the same opened file, so pathname
replacement cannot substitute unchecked content between those operations.
The opened descriptor must belong to the process's effective user. A different
owner is refused before reading content, naming the path and both observed and
expected uid; private permission bits alone do not establish ownership.
The descriptor is acquired without blocking on a FIFO writer, so a selected
FIFO is refused as nonregular rather than hanging before inspection.
Group/other permission bits, a nonregular opened file, malformed JSON, a missing
role or field, an empty value, or a failed credential command are refused.
Open, metadata and read errors identify the operation and path and retain the
underlying cause.
Resolver failures include the command's exit status or terminating signal and
stderr, so an empty stderr does not erase the observed failure. Valid JSON
whose root is not an object is refused with the credential path and the
required role-to-field shape, rather than reported as a missing role.
Within the role object, missing roles, nonobject roles, missing fields,
non-text fields and empty fields have distinct refusals naming the declaration
and file. These diagnostics do not include credential values.
The credential Skryba does not read
writing-style train and rewrite --generator local reach weights without adding a credential domain here: Skryba spawns the installed ster binary and reads nothing to do it. The checkpoint is Ster's problem, and so is any token it needs — Ster resolves a Hugging Face repository through Hugging Face's own cached token file, $HF_HOME/token, by default ~/.cache/huggingface/token, and inherits the environment Skryba was launched with. A private checkpoint with no token there fails inside Ster, with Ster's own message and the hub's status code, not with a Skryba refusal.
Desktop boundary
Skryba Desktop inherits its launch environment, then fills only missing variables from ~/.config/skryba/env. Existing environment values always win. The app passes that merged environment to one installed skryba process and does not parse, store, or transform credentials itself.
Maintained as part of the website-owned Skryba documentation corpus.