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.