On this page
Architecture
Skryba is one Rust process with three external boundaries — Brama, the detector
APIs, and its fleet database — and one local binary it spawns: ster, which
owns every weight in the product. It keeps no database file of its own.
input
├─ draft import ────────> fleet database: drafts
├─ style / language ────> fleet database: choices (the catalog)
├─ score ───────────────> detector ensemble ──> fleet database: scores
├─ rewrite/train ───────> fleet database: choices (resolve the profile)
│ Brama generator
│ Brama quality judge
│ detector ensemble ──> fleet database: scores
├─ rewrite --generator local
│ spawn `ster generate` (adapter attached) ──> candidate text
│ Brama quality judge
│ detector ensemble ──> fleet database: scores
├─ benchmark ───────────> fleet database: choices + Brama generator + quality judge
├─ writing-style ───────> fleet database: passages, preferences
│ ├─ export ──> Ster export file
│ └─ train ───> Ster export file
│ ──> spawn `ster tune sft|dpo` ──> LoRA adapter
└─ database check|drop-schema
The fleet database is stado database resolve skryba: Stado names the credential item skryba-database, Skarbiec hands its pooler_url and ca_certificate to the skryba-database-client bearer, and Skryba connects over TLS verified against that CA (database).
The writing-style branch calls no model and no detector: add, keep,
export and status read the person's own files and write their own rows
and their own export, and train writes the export and then spawns a process.
The process it spawns is another matter — Ster resolves the checkpoint and
fetches it from the Hugging Face hub when it is not already cached, so train
and rewrite --generator local reach a network through Ster even though no
Skryba code does. The same holds for model credentials: Skryba reads none of its own
here, and Ster reads Hugging Face's own cached token file
($HF_HOME/token, by default ~/.cache/huggingface/token) when it fetches a
private repository.
What Skryba owns
- CLI command parsing and local validation;
- detector credentials, provider adapters, selection, normalization, caller-declared request pacing, and cache namespacing; detector and Brama calls are not retried automatically;
- chunking, candidate generation instructions, quality gating, source fallback, and least-detectable selection;
- the style and language catalog: the names a rewrite may require and the instruction each stands for, seeded once and then changed only by
skryba styleandskryba language; - the version-1 native GRPO policy and its atomic JSON persistence;
- curated-corpus checksum and word-count verification;
- the source drafts: whole-file UTF-8 validation, source identity, immutable version retention, and exact text supplied to Score or Rewrite;
- the writing-style rows: ingesting the person's prose, recording the pairs they resolved, writing the two files Ster trains on, and driving the
sterrun that turns one of them into an adapter — locating the binary, building its argv, and reporting the command it ran; - the tables of its schema in the fleet database, created on first use.
What Skryba does not own
- model serving, provider subscriptions, or direct model credentials: Brama owns inference routing;
- the database, its placement and its credential: Stado's database plane declares
skryba, Supabase hosts it, Skarbiec holds the credential item; - local weights and the gradient: Ster maps the checkpoint, runs the passes, writes the LoRA adapter, and generates from it. Skryba links no tensor library and maps no checkpoint in its own process; it writes the training file and drives the installed
sterbinary —writing-style trainrunsster tune, andrewrite --generator localrunsster generate(training boundary); - detector vendors' models or verdict semantics beyond normalization;
- commissioned human work: paying a person to rewrite, verify, or record text runs on the HandToHuman marketplace ledger (
handtohuman market), never through Skryba; - desktop workflow interpretation: Skryba Desktop is a process launcher and view over this CLI.
Module boundaries
| Module | Responsibility |
|---|---|
src/main.rs, src/cli/ |
CLI schema, dispatch, input/output files, database check and drop-schema |
src/app/fleet/ |
resolving the fleet database through Stado and Skarbiec, the TLS connection, the schema, unnamed statements for the transaction pooler |
src/app/drafts/ |
validated source-document import and draft retention |
src/app/profiles/ |
the style and language catalog: seeds, table, validation, profile resolution |
src/app/brama/ |
authenticated /v1/chat/completions client |
src/text/detectors/ |
four provider clients, probability normalization, rate limiting, score cache |
src/text/humanizer/ |
chunking, candidate generation, quality gate, selection |
src/text/quality.rs |
judge prompt, schema parsing, weakest-dimension score, pass rule |
src/text/style/ |
the resolved profile and the instruction block the generator and judge receive |
src/text/writing_style/ |
the writer's own text: passages and preferences, prose ingestion, Ster export shapes |
src/learning/policy.rs |
strategies, sampling, GRPO/Adam update, policy persistence |
src/learning/training.rs |
training data readers and training loop |
src/learning/benchmark/ |
curated-corpus verification and docs-quality benchmark |
src/learning/ster/ |
locating the installed ster binary, ster tune argv and streaming, ster generate as a rewrite generator |
release/docs-paths.sh |
the docs-paths release gate: every src/…/*.rs file this documentation names must be a file of the tree being released |
.wisent-release.json pins this documentation corpus (src/content/docs.json of skryba-landing at one committed revision) as the release input landing-docs, and declares the docs-paths gate as a lint gate reading it, so a Skryba release build or installation refuses a tree whose files the documentation still names after they moved, listing each missing path. A gate run without the input staged is refused, never skipped. After the pages change, the new revision is pinned with stado release catalog pin-input --name landing-docs --source <skryba-landing> --revision <REV> --path src/content/docs.json <skryba>.
Trust boundaries
Input and local files
Text, policies, and corpora are untrusted caller input. CLI paths expand a leading ~ from the home environment or user database; if neither names a home, the command refuses that path with the cause instead of panicking or writing under a literal ~. Paths without a leading ~ are unchanged. Policy and corpus readers validate structure before use.
Brama
The source and candidate text cross the Brama boundary for generation or judging. BRAMA_TOKEN_ROLE=ROLE#FIELD selects the client credential resolved through Stado; optional agent authentication pairs SKRYBA_AGENT_ID with SKRYBA_AGENT_AUTH_SECRET_ROLE. An explicitly selected owner-only credentials file is available without Stado. Secret-valued BRAMA_TOKEN is not read. A response is not trusted: completion presence, truncation, judge JSON shape, numeric ranges, and critical errors are checked locally.
Detector APIs
Source and candidate text cross every selected detector boundary. Each result is accepted only when a recognized field normalizes to [0,1]; a partial ensemble is not accepted.
The fleet database
Drafts, the person's writing, the catalog and detector scores cross into the skryba Supabase project over TLS. The connection string never sits in a file Skryba owns: it is read from Skarbiec on every run with the skryba-database-client bearer, whose grant covers only pooler_url and ca_certificate of skryba-database.
The writer's own files
writing-style add reads paths the person named. A named file is read
whatever its extension and needs no model. A named folder is shown to the
selected model route one folder at a time — the names of its files and
subfolders, never their contents — and only the files it names as prose and
the subfolders it names to open are read; symbolic links are not followed.
The prose goes to the fleet database and nowhere else: no detector call. It
crosses one more boundary on
request — writing-style train writes the person's own passages into the
export file and hands that path to a local ster process, which reads them to
compute a gradient.
The spawned ster process
ster is resolved by search, not configuration: every PATH entry first,
then ~/.cargo/bin, ~/.local/bin, ~/.stado/bin, /opt/homebrew/bin, and
/usr/local/bin — the same order Skryba Desktop uses to find skryba. A
missing binary is one sentence naming the install command. What crosses into
that process is the export file path, the checkpoint name, and the training
knobs; what comes back is an exit status, an adapter on disk, and Ster's own
output, which is never reworded. train streams both of Ster's streams to
Skryba's stderr, so its progress is live and Skryba's stdout stays the single
report object every command answers in; rewrite --generator local captures
Ster's stdout instead, because there the completion is the output. A zero exit
that wrote no adapter is refused rather than reported as success.
Durable state
The scores table contains hashes and probabilities, not original text. The drafts table deliberately retains whole imported documents so Score and Rewrite can reuse them; the passages and preferences tables are the other deliberate exception: they keep the person's prose,
because a hash cannot be trained on. The choices table holds names and instructions the person wrote or kept; every rewrite report copies the instructions it used, so a report stays readable after the catalog changes. Policy JSON contains strategy instructions and optimizer state. The Ster export and the LoRA adapter are files, at paths the operator names: Skryba writes the export, Ster writes the adapter, and neither is cleaned up afterwards — the export is kept beside the adapter on purpose, so the file that produced it stays next to it. None of these contains an API key. Credentials remain in the process environment (or, for Desktop launches, the user's ~/.config/skryba/env) and in Skarbiec.
Maintained as part of the website-owned Skryba documentation corpus.