Architecture
Skrzynka is one local Rust process using the Stado-resolved fleet database for durable mail state, Skarbiec for secrets, IMAP/SMTP for provider transport, and central Wisent identity for organization-scoped HTTP access.
Overview#
Skrzynka is one local Rust process using the Stado-resolved fleet database for durable mail state, Skarbiec for secrets, IMAP/SMTP for provider transport, and central Wisent identity for organization-scoped HTTP access.
CLI (--organization <ID>) ┐
v
Desktop -> loopback HTTP -> AppState -> stado-database -> fleet database skrzynka
| | |
| | +-> Skarbiec -> credential JSON
| | (connection lifetime)
| +----> IMAP read / SMTP reply or originate
+-> central identity verification
Google browser -> loopback OAuth callback -> Google token/identity endpointsWhat Skrzynka owns#
- mailbox references and non-secret connection profiles;
- organization-scoped rows in the fleet database;
- synchronization cursors and normalized message copies;
- reply and outbound intent, full plain-text bodies, idempotency, delivery evidence, and uncertain recovery;
- loopback
/v1enforcement and provider adapters in this repository.
What it does not own#
- Skarbiec: encryption, vault availability, credential recovery, rotation, and revocation;
- mail provider: mailbox existence, IMAP UIDs, folders and flags, SMTP acceptance policy, delivery, and provider retention;
- shared identity: users, bearer sessions, and organization membership;
- skrzynka-desktop: presentation and first-use screens;
- Echo/Stado: optional onboarding definition/event transport.
Skrzynka does not generate replies, manage folders, mark or delete provider messages, retain attachments or remote HTML, or expose a remote shared service.
Durable state#
Skrzynka resolves the fleet database named skrzynka through the shared stado-database client, Stado and Skarbiec. Opening it creates missing tables and refuses a schema newer than this binary supports. Mailboxes hold organization and Skarbiec references; retained messages, replies and outbound records belong to those mailboxes. Startup turns interrupted sending records into uncertain. There is no local SQLite fallback or SQLite migration command in the current CLI; preserve any legacy database until a supported migration is available.
The database contains normalized inbound message bodies, reply bodies, and every originated message's full plain-text body, recipients, cc, subject, delivery status, provider message id, and refusal. It must be treated as sensitive. It does not contain mailbox passwords, OAuth tokens, Wisent sessions, or membership records. Backups need the same protection as the inbox and sent-mail evidence.
Concurrency and scheduling#
One AppState owns an async operation mutex. Synchronizations and SMTP work are serialized in-process. Each imported message and its cursor are committed together, so a later provider failure preserves prior progress. Enabled mailboxes wait for IMAP IDLE notifications after synchronizing, without a mailbox poll interval. Separate processes sharing one database are unsupported.
Database resolution or connection failures return DATABASE_UNREACHABLE with the underlying failed step and error; CLI and HTTP expose the same cause. When a qualification journey isolates HOME, SKRZYNKA_FLEET_HOME identifies the account whose Stado configuration and database bearer it should use. Back up the fleet database through its owning service, not local SQLite companion files.
Trust boundaries#
Loopback HTTP
serve rejects non-loopback IPs before binding. Loopback is an exposure boundary, not authentication: all /v1 resources except Google's callback still require a verified bearer and organization header. Health exposes only versions/readiness.
Credentials
The exact Skarbiec item is fetched only when profile validation or a connection needs it. Child execution is bounded to 15 seconds. Secrets reside transiently in core memory and the connection task. The API models and schema contain no password field.
Provider network
IMAP uses direct TLS and BODY.PEEK[]; synchronization is read-only but the current IMAP library call has no configured network timeout. SMTP uses required STARTTLS or implicit TLS and a 30-second transport timeout. SMTP submission is a real external mutation; an ambiguous terminal response is never retried automatically.
Gmail
OAuth callback state binds the external browser redirect to one in-memory organization flow. Endpoint allowlists require canonical Google hosts. Workspace delegation grants the service-account client ID access to every authorized user in the domain, so its Skarbiec item is a domain-wide credential.
Failure isolation#
All-mailbox sync embeds each mailbox failure and continues. Local messages remain readable when Skarbiec, identity, or a provider is unavailable. HTTP identity verification fails closed. A failed SMTP attempt is durable; an ambiguous attempt becomes uncertain so recovery cannot silently duplicate mail.