Persistent service and receiving diagnostics
Install the loopback core through Stado, distinguish process readiness from mailbox authentication, and import every INBOX message without skipping older mail.
Install the service, not a background shell#
Skrzynka Desktop is a client of the local core. It does not launch a second core or read the database directly. Stado owns the persistent service declaration and supervision. Read the current machine's registered name with stado registry self, then replace HOST below with that name.
stado registry self
stado product install skrzynka --surface service --host HOST
stado service show skrzynka --json
curl --fail --show-error http://127.0.0.1:8788/healthzThe canonical product catalogue installs the Skrzynka binary and calls Stado service ensure. The declared program uses the account's installed Skarbiec executable and existing local state. Repeating an installation does not create another mailbox database or another manually managed process. Do not run a foreground skrzynka serve against the same database while the managed service is running.
The default is loopback port 8788. Another product already using that port is a conflict, not permission to stop it. Choose an unused loopback port in the host's declared service and save the matching address on Desktop's Service screen. The core refuses non-loopback binds.
Read what each check actually proves#
| Check | Evidence | Does not prove |
|---|---|---|
| stado service show skrzynka --json | The persistent service declaration and its reported runtime state | A working mailbox or authenticated API consumer |
| GET /healthz | The loopback core responds with its version and database schema | A valid Wisent session, organization membership, or Google credential |
| skrzynka status: skarbiec_available | The configured Skarbiec command answers its version probe | That any credential can be decrypted or that Gmail accepts it |
| skrzynka sync --mailbox ID | The real IMAP operation and its persisted mailbox result | SMTP acceptance or recipient delivery |
Desktop's Service screen distinguishes Not checked, Available and Unavailable. Its Managed service section gives the installation commands without executing a shell. After installation, Check service repeats the HTTP checks. The mailbox's Sync result remains the authority for receiving failures.
CLI mailboxes belong to the local legacy namespace. Authenticated API and Desktop resources belong to the selected Wisent organization. A healthy core with an empty organization is not evidence that the existing local mailbox was deleted.
Repair the credential without hiding the refusal#
GMAIL_IMAP_PASSWORD_REJECTED means Google rejected the selected password. An ordinary Google sign-in password is not a Gmail app-specific password. Repeating sync, restarting the core, or changing the SMTP server does not repair that refusal. Use a valid app-specific password through the documented stdin-only skrzynka account app-password --provider gmail --email ADDRESS path, an existing Skarbiec app-password item through Desktop Connect, or the documented Desktop-app OAuth client path.
Password login failures append IMAP LOGIN at HOST: DETAIL to the core error message. DETAIL retains the IMAP library's observed response code and explanation, with the submitted password removed. A Gmail NO response remains GMAIL_IMAP_PASSWORD_REJECTED; a connection failure instead reports IMAP_AUTHENTICATION_FAILED. An AUTHENTICATIONFAILED response alone does not distinguish a wrong account password from an invalid app-specific password. Read the CLI error, API error message or saved mailbox last_error_message rather than inferring a cause from password length.
The core verifies the app-specific password against Gmail IMAP before saving it. The optional --mailbox ID_OR_ADDRESS selector attaches receiving credentials to an existing mailbox while preserving its outgoing identity. Without that selector, an unambiguous matching Gmail address is reconnected. A missing or ambiguous selector leaves both the vault and mailbox unchanged.
A Google request for account verification, a missing app-password capability, or an invalid OAuth client remains a prerequisite failure. No mailbox connection is reported as successful until the real provider accepts it. Workspace delegation cannot authenticate a personal @gmail.com account.
Import every message without skipping older mail#
skrzynka --organization ORGANIZATION mailbox declare --skarbiec-item ITEM_ID
skrzynka --organization ORGANIZATION sync --mailbox MAILBOX_ID
skrzynka --organization ORGANIZATION message list --mailbox MAILBOX_IDEach declare or sync takes every remaining INBOX UID in ascending order. IMAP SEARCH returns a set, whose iteration order is not chronological, so the UIDs are sorted first: each message is then fetched, normalized and committed with the UID cursor past it, and a cursor can never jump over an older unprocessed row. The API, Desktop Declare and scheduled poller use the same receiver.
One pass imports everything there is; there is no page and no continuation flag. An authentication failure advances nothing. A fetch failure part way keeps every message committed before it, so the next sync continues from the cursor. Desktop Sync shows Complete or Failed per mailbox. Existing operator messages are not marked, moved or deleted at the provider.
A FETCH row must identify the requested UID. Unsolicited rows do not replace that message. If a nonempty response has no matching UID, IMAP_FETCH_UID_MISMATCH names the endpoint, requested UID and observed UIDs without advancing the cursor. CLI, API and Desktop receive the same refusal; earlier committed messages remain available.
Sorting prevents future cursor skips. It does not claim to restore mail already skipped by an older build. Do not reset an operator's cursor or replace their database as an incident workaround.
Qualify a whole-backlog import against a real mailbox#
node tests/mailbox/backlog.mjs --organization TEST_ORGANIZATION --mailbox TEST_MAILBOX_ID --fixture PATH_TO_EXPECTED_MESSAGES_JSONRun this from the Skrzynka repository with a dedicated, already connected real test mailbox. The fixture file is an array of expected provider messages, each with external_uid, message_id, subject and body_text, and more than one of them must remain beyond the mailbox's current cursor. The expected values must come from the real provider fixture, not a simulated response. The runner never creates a fake provider, sends mail, changes tags, deletes data, or resets a cursor. A consumed fixture needs a new real backlog before another qualification.
The backlog and Gmail-connection qualification runners stream child stdout and stderr directly to run-owned files before decoding results. They impose no separate subprocess-output byte ceiling; file write/read failures remain failures rather than truncated evidence. Complete private outputs stay in the run report directory.
The runner syncs once and requires that one pass to reach the highest pending UID with no continuation flag, verifies the saved cursor through mailbox show, reads every expected body through a separate message show process, checks that another sync does not duplicate fixture UIDs, and requires a cross-organization read to return NOT_FOUND. A pass that stops short, skipped or duplicated UIDs, incorrect bodies, and a fixture without a backlog fail the run. Reports under .build/real-tests/mailbox-backlog retain source revision and diff, executable and fixture digests, commands, exit statuses and output. These private reports can contain message bodies; do not publish them as public documentation. An incompatible installed CLI or unavailable real dependency is a failed qualification, not proof of the source repair.
Maintain the installation declaration#
The source declaration lives at skrzynka/deploy/service.json. Update the canonical product record through stado product registry set skrzynka --service-file PATH. The registry command validates the whole record before writing it to stado/catalog/products.yml. Stado compiles that catalogue and derives its service catalogue from it, so the change reaches a host with the next Stado release; stado product catalog --output PATH writes the derived service list for inspection. A Stado binary built before the change may not yet recognize the product service name.