No description
Find a file
randogoth 73408bdf24
Some checks failed
CI / macos (push) Successful in 39s
Release / macos (push) Failing after 33s
CI / linux (push) Has been cancelled
Release / linux (push) Failing after 3m11s
Release / release (push) Has been skipped
Add tag-triggered release workflow
Builds macos-arm64 and linux-x86_64 release binaries in parallel and
uploads them as build artifacts; a single release job gated on both
then downloads and publishes them to one Forgejo release. Splitting it
this way avoids a real race in actions/forgejo-release itself: its
script always does a blind create-then-upload for the tag with no
merge path, so two parallel callers against the same tag either
hard-fail or stomp each other's uploaded assets.
2026-10-07 20:03:18 +03:00
.forgejo/workflows Add tag-triggered release workflow 2026-10-07 20:03:18 +03:00
data feat: embed the carrier icon in the Windows exe 2026-10-03 14:01:10 +03:00
scripts refactor: migrate the UI to GTK4 with Relm4 and libadwaita 2026-10-03 00:40:53 +03:00
src feat: ask whether a reply carries the sender's frontmatter 2026-10-07 13:10:09 +03:00
windows feat: add a one-command windows build script 2026-10-03 12:31:19 +03:00
.gitignore feat: add a one-command windows build script 2026-10-03 12:31:19 +03:00
build.rs feat: embed the carrier icon in the Windows exe 2026-10-03 14:01:10 +03:00
Cargo.lock feat: embed the carrier icon in the Windows exe 2026-10-03 14:01:10 +03:00
Cargo.toml feat: embed the carrier icon in the Windows exe 2026-10-03 14:01:10 +03:00
com.randogoth.carrier.json feat: package the app as a flatpak 2026-10-03 12:17:15 +03:00
devbox.json feat: make devbox.json cross-platform for GTK4/libadwaita 2026-10-07 19:45:20 +03:00
devbox.lock chore: update devbox.lock for gtk4/libadwaita on Linux 2026-10-07 19:47:41 +03:00
README.md feat: ask whether a reply carries the sender's frontmatter 2026-10-07 13:10:09 +03:00

carrier

A minimalist desktop client for Smol Mail: one compact window — the list above, the article below — built on fumi-core, the reference CLI's own library, with a Relm4 (GTK4) UI built from libadwaita's own widgets, so it follows the user's theme and accent colour. One 32-byte seed is the only secret; mail stays sealed at rest and opens on demand.

What it does

  • Create or restore an identity from a seed, shown once at creation time.
  • Pin a server's key — paste the operator's, or connect and pin what it presents — then register a username or recall an existing address.
  • Fetch (with cancel), read, reply and send sealed mail; the inbox, the requests tier and sent copies each keep their own folder.
  • Composes closed by any sidebar trip are stashed as drafts — kept beside the store, reopened from a folder that appears only once one exists; Cancel is the only way to throw one away.
  • Custom Key: value frontmatter rows when writing: one line per header under the subject, validated against the frontmatter rules — the client's own fields (subject, reply-to, in-reply-to, accept) can't be shadowed, and rows survive a stash as part of the draft. Received mail shows the sender's own fields in the message detail, between the meta line and the body; replying to such a mail asks whether to carry copies of the fields into the reply, where they stay editable.
  • Accept or block a requester when their mail carried a self-certifying reply address; accept tokens attach to accepted contacts automatically.
  • Contacts with verified/tofu marks and smol:// import; your own identity opens in the same detail view, its address in the header bar with a QR share beside it, key, fingerprint and URI below.
  • Key rotation with a confirm-first step, server pin management, a network timeout, and gsmol backup export/import that never contains the seed.

Layout

src/main.rs   config directory, demo seeding, launch
src/app.rs    state and update; blocking calls run on the worker pool
src/view.rs   the Relm4 component: widgets, the op adapter, the re-render
src/ops.rs    fumi-core calls, key/read-mark files, and the offline tests

State

Everything lives under $XDG_CONFIG_HOME/carrier (default ~/.config/carrier): carrier.key is the master seed, written 0600; carrier.db is fumi-core's sealed store; read.txt is carrier's own read marks, one hex id per line.

Run and verify

devbox run run      # build release and launch
devbox run check    # fmt --check, build, tests
devbox run test     # tests only

The tests run offline against an in-memory store: seed and key parsing, a sealed envelope listed and opened end to end, contact import, a full backup round-trip, pin labels and the date formatter. scripts/devbox-libs.sh puts the GUI stack's runtime libraries (wayland, xkbcommon, EGL, vulkan) on the library path inside the devbox shell — they are dlopened, so nothing that links the binary can provide them.

Status

Work in progress: the UI is unexercised against a live server — the store, crypto and address paths are covered by tests, the network flows (register, fetch, send, rotate) are wired to fumi-core's operations but only smoke-tested as far as launching the window.

Packaging

devbox run package builds and installs the app as a Flatpak from com.randogoth.carrier.json — the binary, the desktop file, the metainfo and the icon all come from the repo, and the tooling (flatpak, flatpak-builder, appstreamcli) lives in the devbox environment. The flatpak runtime is the one dependency outside devbox; install it once:

flatpak install --user flathub org.gnome.Sdk//51 org.freedesktop.Sdk.Extension.rust-stable//26.08

The result is installed into the user flatpak and runs as flatpak run com.randogoth.carrier. Inside the sandbox the app keeps its config under ~/.var/app/com.randogoth.carrier/config/carrier, so a flatpak install starts with a fresh identity rather than the dev build's account.

Windows

Install MSYS2 (winget install MSYS2.MSYS2), open the MSYS2 MINGW64 shell, and run the one-command script from the carrier checkout:

git clone ssh://git@code.randogoth.com:2222/randogoth/carrier.git
cd carrier && windows/setup.sh

windows/setup.sh installs the GTK and Rust stack into MSYS2, clones fumi next to carrier if it is missing, builds, and bundles carrier.exe with the GTK runtime into dist/ and a zip — the complete Windows distribution. The app keeps its config in %APPDATA%\carrier. If MSYS2 updates its core mid-run the shell may close; rerun the script and it continues. Cloning over SSH needs the key in ~/.ssh inside the MSYS2 home.