Astraea is an offline-first, privacy-first calendar. Events live on your device, are end-to-end encrypted with your own Nostr key (NIP-44), and sync through relays you pick yourself — no account, no server of ours.
This release supersedes the unpublished 0.4.0 and carries everything since 0.3.1.
✨ Sign in without ever handing over your key
NIP-46 remote signer ("bunker") login, on Android and Linux.
Paste a bunker:// connection string and the account private key never reaches the device. Astraea stores only a throwaway client key that your signer authorizes by public key — and can revoke at any time.
On Linux this is a login and unattended background signing, with no key material on the machine at all. It replaces the old advice to provision a local delegated key:
astraea-service auth connect-bunker # paste bunker://… on stdin
astraea-service auth status # signer: remote_nip46, state: readyIn the apps it's a "Sign in with a remote signer" field in Settings → Account (and in first-run onboarding on Android).
Every reply from the signer is checked before it is trusted: kind, author, NIP-01 event id and Schnorr signature — and for a signed event, that every field still matches what Astraea asked to have signed.
Implemented on top of the already-audited NIP-44 and relay-pool code rather than pulling in a NIP-46 client crate, whose dependency subtree compiles deprecated NIP-04 AES-CBC in as a feature-flag side effect. Only the JSON envelope handling is ours. See
docs/authentication.md.
🔄 Sync when the app opens
Every launch now reconciles with your relays before you touch anything, on mobile and through the Linux service alike.
Amber (NIP-55) sessions stay manual on purpose — signing there is an intent into another app, so an automatic cycle would throw you into Amber on every launch.
🖼️ Android widgets finally preview properly
The widget picker showed an empty placeholder grid instead of the widget. The three providers declared no preview at all, so the picker fell back to inflating the live layout — whose list/grid has no data source outside the app.
Each widget now ships a previewLayout (API 31+) with static sample content, plus a rendered previewImage for API 24–30. The sample data is fictional on purpose: binding your real calendar into a preview would put your event titles in the launcher's widget picker.
🔒 Security & hardening
| Area | Change |
|---|---|
| Relay responses | Added a total memory budget. The existing per-event and per-count caps multiplied out to hundreds of megabytes a hostile relay could make the app hold. |
| Kairos local socket | Now bounds its read buffer while reading instead of after the fact — a peer that never sent a newline could previously be buffered without limit. Adds a connection cap and per-connection backpressure. |
| Relay list | Capped at 16 on both the service and the mobile store, on read as well as write. Every relay is an open socket, a publish fan-out target, and another operator learning your pubkey and IP. |
| Sign-out | Now also clears the cached profile and on-disk avatar, drops the live remote-signer connection, and closes bunker sockets. |
| Logs | No longer record the account public key, deep-link targets or full avatar URLs — each ties a device log to a specific identity. |
| External signers | Signed events from any external signer are verified field by field against the request, not only checked for a valid signature. Previously this held for Amber only. |
🐛 Fixed
- Overlapping sync cycles: the start-up sync racing a manual tap could run two full pull-merge-push passes at once over the same store.
- The automatic sync is a start-up action rather than a "calendar screen mounted" action, so returning to the calendar no longer re-triggers it.
- Version numbers agree again across
pubspec.yaml, both Rust crates, AppStream and the RPM/Arch packaging — 0.3.1 had bumpedpubspec.yamlalone.
🧹 Removed
- The placeholder
RemoteSignerbackend that reported "not configured in this build" for every operation.
📦 Install
Android
Download astraea-0.4.1.apk below and install it. Minimum Android 7.0 (API 24).
Linux (Debian / Ubuntu / Pop!_OS)
Download the .deb files plus install.sh into the same folder, then:
chmod +x install.sh
sudo ./install.sh # add --no-extension on KDE/COSMICThe packages stay separate on purpose (astraea-service, astraea-desktop, astraea-gnome-shell-extension) so you can skip the GNOME extension and upgrade each part on its own. Passing them all to one apt install is what makes the dependency order correct.
Or, without cloning the repo:
curl -fsSL https://raw.githubusercontent.com/Lwb89dev/astraea/v0.4.1/scripts/install-release.sh | sh🔑 Checksums
Verify before installing:
bfa55b04d4ba50bf1bb034ad7ea1c6d04462850cf52313585616b5f2df378bbb astraea-0.4.1.apk
23f17e46a46a52bcd4d0a8d674e8d01aaa2d1249cccfe4ccf950b8b2de400b43 astraea-service_0.4.1_amd64.deb
ed61eceb39c55a88a0ca46604a1c13f6b03666a0a4f0cf9f9ae859af1538970e astraea-desktop_0.4.1_amd64.deb
fe5b09ade229fb730b39f45f3b92444b41240dba3f901d371a0715b8da0413ff astraea-gnome-shell-extension_0.4.1_all.deb
1e63a5f1e2f9ab991921cc384ec3b246a995fb1a58282de3ff790cf1e47223c2 astraea-all_0.4.1_all.deb
The APK is signed with the project release key:
SHA-256 E2:A9:EA:50:E0:BB:39:40:06:BD:A3:A0:10:AA:FD:C2:38:ED:06:9E:7A:A2:91:61:95:F2:55:2F:5F:7E:C0:18
Full changelog: CHANGELOG.md · Licence: GPL-3.0-or-later