Skrzynka Desktop journeys

These canonical journeys cover the native application outcomes in version 0.2.x. They use shared Wisent authentication and the real authenticated loopback Skrzynka API. Provider-facing steps call out their side effects before the operator a

Overview#

These canonical journeys cover the native application outcomes in version 0.2.x. They use shared Wisent authentication and the real authenticated loopback Skrzynka API. Provider-facing steps call out their side effects before the operator acts. Destination names below are the ones in the sidebar; the interface design contract states why each exists.

Coverage#

ActorOutcomeStarting stateRiskJourney
New operatorComplete one provider-backed mailbox synchronizationShared sign-in available, core running, no desktop progressCredentialed readFirst mailbox
OperatorAdd another mailbox from SkarbiecWindow openLocal mutationAdd another mailbox
OperatorRead one mailbox or unified inboxAt least one synchronized mailboxRead-onlyRead mail
OperatorSend one reply through the source mailboxSelected inbound messageProvider-facing mutationReply
OperatorOriginate one message from a connected mailboxAt least one connected mailboxProvider-facing mutationCompose
OperatorAudit what this organization sent and resolve an unclear sendAt least one originated messageRead-onlyOutbound
OperatorPause or remove local mailbox stateExisting mailboxDestructive local mutationPause or remove
OperatorRecover from unavailable core or providerDependency error visibleNo additional side effectRecovery
OperatorGenerate reply or message text, render HTML, manage attachments or provider foldersAny—Not supported by this release

First mailbox from Skarbiec#

Goal: complete one real provider synchronization without exposing the mailbox password to the app.

Preconditions: macOS 14+, exact desktop source build, Skrzynka core 0.2.x ready on loopback, a shared Wisent identity with a selected organization, and one login or bundle Skarbiec item. The provider inbox contains a known non-sensitive message.

  1. Open Skrzynka Desktop and complete shared Wisent sign-in and organization selection. The window opens on Posture.
  2. Read the Posture signal strip. Service carries the core's version; Skarbiec must read Available. Mailboxes reads "Not configured" in neutral, because nothing configured is not an incident.
  3. In Needs attention, the row "No mailbox is configured" carries the verb Connect. Choose it.
  4. On Connect, pick one of the three account-type facets. For a Google account, select the address and read the guard sentence before authorizing. For a Gmail app password, select the exact Skarbiec item that contains it. For a Saved account, filter items and select the exact one; keep the bundle profile for a complete bundle, or supply email, IMAP host/port, SMTP host/port and TLS mode for a login. No password field exists on any path.
  5. Choose Create mailbox for a saved account, Connect Gmail app password for the app-specific-password path, Continue with Google to start loopback OAuth, or Connect with Google Workspace for domain-wide delegation. The mutation bar reports the outcome; for OAuth it names every state of the core's flow as it changes. Read the measured connection report and Gmail connection for prerequisites and refusals.
  6. Go to Sync and choose Synchronize now, or use the per-mailbox Synchronize now in the Mailboxes inspector. First success is the completed provider-backed cycle; a provider that truthfully returns zero new messages remains an honest empty inbox.
  7. Confirm on Inbox: the message carries its receiving mailbox in the first column.

Failure: the screen names the refusal in plain language — what the core's code means, and what to do about it. An item Skarbiec already declares reads Already a mailbox, and declaring it again keeps retained messages. Correct the authoritative state in Skarbiec, then retry.

Cleanup: Mailboxes inspector → Remove mailbox, which opens a decision dialog listing exactly what is destroyed and what survives. Skarbiec and provider state remain.

Add another mailbox#

Open Connect, select another exact Skarbiec item, then Declare as mailbox. The item must already hold its complete profile in Skarbiec; the app never edits account values.

Read mail#

On Inbox, the facet rail scopes the list: Every mailbox, or one mailbox. Selecting a mailbox facet re-reads the server scoped to that mailbox rather than filtering the loaded window. Select a row to read plain text, the receiving mailbox, original sender/recipient headers and the Reply-To the core will target. Remote HTML and attachments are intentionally absent.

Reply through the source mailbox#

Risk: this submits a real SMTP message to the original sender or Reply-To address.

  1. On Inbox, inspect the selected message: receiving mailbox badge, Reply-To, recipients and body.
  2. Enter plain text in the composer. Its header reads Too long once the body passes the real 256 KiB limit, and the send verb stays disabled while it does.
  3. One idempotency key is minted for this attempt. The reply composer does not show it: a refused reply offers Try again as new reply, which mints another, because a key the core has already answered is spent.
  4. Choose Review and send. The decision dialog names the sending address in its title, lists To and Subject, and states that sending cannot be undone and that an unclear result means checking Sent before trying again. Confirm with Send reply.
  5. Sent means SMTP accepted the message. Failed is safe to inspect before a new attempt. Uncertain requires checking provider Sent mail; the app never resends it automatically, and a retry needs a new key. All three are auditable on Replies.

The provider message cannot be recalled by Skrzynka.

Originate mail from a mailbox#

Risk: this submits a real SMTP message to the addresses typed here. No inbound message stands behind it, so nothing but this screen decides who receives it.

  1. Open Compose, or New message on Outbound. The draft lives on the model rather than in the screen, so a click on another destination does not lose it.
  2. Pick the sending identity in the Send as rail. Every connected mailbox is offered, including a paused one: enabled governs synchronization only, and the inspector states that sending from a paused mailbox still works.
  3. Fill To — one address, or several separated by commas — the optional Cc, the required Subject, and the plain-text message. Nothing is parsed here: the core splits on commas, validates every address, and refuses the whole request before it claims a row.
  4. Read the inspector: the sending identity, the outgoing server and port the core will use, the idempotency key for this draft, and the rule that Skrzynka never sends one key twice.
  5. Choose Review and send. The decision dialog names the sending address in its title, lists To, Cc when present, and Subject exactly as typed, and carries the key as its footnote. Confirm with Send message.
  6. Sent reports the recipients and the provider message id when SMTP returned one, and the composer keeps that mailbox with a fresh key. Failed and Uncertain are the core's verdict on a row it had already claimed. Pending and Sending say the send is still in flight and that Outbound shows how it ends.

Failure: the refusal is the core's machine code beside the core's own sentence — OUTBOUND_RECIPIENT_INVALID naming the address it rejected, OUTBOUND_SUBJECT_INVALID, OUTBOUND_BODY_TOO_LARGE against the 256 KiB limit, or SMTP_REJECTED carrying the SMTP server's own text. The key that reached the core is spent, so the screen offers Start a new message, which mints a new key and keeps what was typed.

Cleanup: none exists. Skrzynka cannot recall a message SMTP accepted, and the row remains as the only local evidence that it was sent.

Audit what went out#

Goal: read what this organization originated, and resolve the sends whose provider-side outcome is unknown.

Outbound is a fleet-wide read. GET /v1/outbound answers scoped or unscoped, so the newest 300 originated messages arrive in one request on every refresh and the screen needs no per-message sweep of the kind Replies requires. Selecting a mailbox re-reads the server scoped to it, as on Inbox.

Facets are Needs attention — the default, because sent is not why anyone opens this screen — then Every message, May be sent, Failed, Sent and Sending, each with its count. The table is status, mailbox, recipients, subject and last update; sent is plain text and only a state that needs a human gets a pill.

The inspector carries the refusal code and the core's sentence, the sending identity, recipients, Cc, the provider message id, the idempotency key, the created, updated and sent instants, and the stored body — the message as it was actually sent.

An uncertain send raises a standing warning above the table naming the mailbox whose Sent mail to check, a Posture panel, and a sidebar count beside Outbound that it shares with failed sends. Nothing here resends a key: an unresolved row offers Compose a new message from this one, which prefills a new draft with a new key, because a second copy at the provider is a decision only the operator can make.

Pause or remove a mailbox#

On Mailboxes, select the row. Pause synchronization leaves all local state readable but excludes the mailbox from every sweep and from the background poller. Remove mailbox opens a decision dialog naming the exact cascade — the mailbox, its downloaded messages, its reply history, and every message it sent — and what survives, namely its Skarbiec items and provider mail. Schema 4 retains the outbound_messages cascade introduced by schema 3, which is often the only local record that a mailbox originated anything at all; back up the core database first when that evidence must be retained, at the path skrzynka status reports.

Recovery#

  • Core offline: Service → the saved loopback address, with Check service as the retry. Starting the service is not something this app can do: it supervises no process.
  • Skarbiec unavailable: Posture shows this as danger, not a warning, because it is the credential authority and every sync will fail without it. Restore the supported local Skarbiec executable and key access; existing local messages remain readable throughout.
  • Mailbox error: the mailbox appears in the Mailboxes Failing facet, in a Posture panel and in a Sync panel. The Mailboxes inspector states what the stored code means and what to do; the Posture panel adds the verb, and for a credential refusal that verb is Reconnect rather than Retry.
  • Interrupted reply: Replies → May be sent facet. The inspector names the mailbox whose Sent mail to check before any new explicit attempt, and a retry needs a new key.
  • Unclear send: Outbound → May be sent facet. The inspector names the mailbox whose Sent mail to check, and the only verb offered composes a new message under a new key.

Return to the README, interface design contract, first-use journey, or architecture.