GREEN. rustls 0.23 selects its CryptoProvider from its own 'ring' /
'aws-lc-rs' crate features. Arti reaches rustls through tor-rtcompat with
default-features = false and nothing in this workspace turned a provider
feature on, so rustls was compiled with zero providers and the first TLS
config built from the process default panicked:
Could not automatically determine the process-level CryptoProvider from
Rustls crate features. ...
That is the 'Failed to start' screen on the unlock path. The 'ring' crate
being present in the graph (via snow, for Noise) never had anything to do
with it.
'ring', not 'aws-lc-rs': aws-lc-rs wants CMake and a C toolchain for the
NDK and fights the cross-compile.
Belt and braces: open_wire() now calls install_crypto_provider() before
Node::start_with_passphrase, a OnceLock-guarded
ring::default_provider().install_default() with the already-installed Err
ignored. That keeps the cdylib correct regardless of how a consumer's
feature graph resolves rustls.
aws-lc-rs is absent from the lock, so there is no two-provider conflict to
report.
Evidence:
cargo test --release --test provider_resolution
before: panicked at rustls-0.23.44/src/crypto/mod.rs:249 (device message)
after: 1 passed
cargo tree -f '{p} [{f}]' -p rustls
rustls v0.23.44 [log,logging,ring,std,tls12]
RED. The APK builds, installs and opens, but the first rustls config built
from the process default panics one call deep inside Arti's rustls backend:
Could not automatically determine the process-level CryptoProvider from
Rustls crate features.
Call CryptoProvider::install_default() before this point ...
rustls 0.23 picks its provider from its own 'ring' / 'aws-lc-rs' crate
features, not from which crypto crates happen to be linked. Arti reaches
rustls via tor-rtcompat with default-features = false, so neither provider
feature is on and rustls compiles with zero providers. 'ring' does appear in
cargo tree, pulled in by snow for Noise -- unrelated and irrelevant.
This commit adds rustls as an explicit dependency with no provider feature
(no behaviour change: it is already in the graph that way) so the test can
name the type, plus the regression test. On the host it reproduces the
device's panic verbatim.
The SDK lockfile is resynced with the root Cargo.toml on main (onionwire
0.2.1, rpassword, md-5, rtoolbox) in the same commit so the test build is
against a consistent lock.
wait_until_published now logs HsId via safelog, not the locator.
Store and client_config mkdir Arti cache/state 0700. Arti
dangerously_trust_everyone stays on storage only.
Refuse unauthenticated ONIONWIRE_WALLET_RPC, speak HTTP Digest like
monero-wallet-rpc --rpc-login, cap responses at 1 MiB, and reject
.onion RPC URLs instead of half-dialing them over TCP.
Overlapping Actions used a fixed docker name onionwire-ci (Conflict)
and OOM-killed with 137. Unique run_id names + one-at-a-time
concurrency. F1 ingest test still used 8+B*94 which F4 checksums reject.
Reject non-32-byte spk, non-v3 onion, and invites over 4KiB before
hex alloc. Sign onionwire-invite-v1 with 0x00 separators; verify that
first. Well-formed v1 concat still verifies after field checks.
ChaCha20-Poly1305 at rest now MACs owmsg1||friend_id_le64||dir||0x00||row_id_le64.
Swapped ciphertext fails closed. Empty-AAD v0.2 blobs rewrap once on unlock;
list_messages never falls back to empty AAD.
Adds the Android product alongside the existing Linux TUI, in one repo.
SDK — crates/onionwire-sdk is a UniFFI facade over the very same `onionwire`
crate the TUI runs on. Identity, invite, friend upsert, send/receive, rotate
and wipe all delegate; no protocol is reimplemented, so an Android peer and a
Linux peer interoperate. It is its own Cargo workspace because Arti's TLS
backends are non-additive: the TUI keeps native-tls (OpenSSL), Android needs
rustls + static-sqlite (no OpenSSL, no system libsqlite3 in the NDK).
android/ — Gradle project. :sdk produces the AAR (Kotlin bindings generated at
build time + libonionwire_sdk.so via cargo-ndk), :app is a Kotlin/Compose/M3
messenger depending on :sdk only. minSdk 26, targetSdk/compileSdk 36,
INTERNET-only, data in filesDir, backups excluded.
Root Cargo.toml grows `native-tls` (default) and `rustls` features so exactly
one Arti TLS backend is selected per build graph. The default build is
unchanged: same backend, ratatui still a normal dependency, src/tui.rs
untouched.
Also: Store::self_fingerprint/set_petname + Node wrappers (additive only),
scripts/build-android-local.sh, README sections, .gitignore for local SDK paths.
Wrap a random 32-byte message key with Argon2id+ChaCha20-Poly1305.
sqlite messages.plaintext is nonce||ciphertext; identity keys stay
plaintext. Empty/wrong passphrase fails closed. Ctrl-Q types CLEAR or
QUIT then Enter (Esc cancels).
Arti combined status stays Bootstrapping through a 5 min HsDir upload
round. Treat a successful connect as published, floor cbtinitialtimeout
with cbtmintimeout at 20s, and skip preemptive 80/443 circuits so IPT
and HsDir builds are not starved. PUBLISH_WAIT stays 360s fail-closed.
Signed inv/rcp codecs keyed by identity, payments table with unverified
incoming receipts, optional JSON-RPC sidecar (loopback/.onion HTTP only),
and TUI /pay /tip. OnionWire still holds no spend keys.
prf frames are ed25519-signed, friend-visible only (unknown pubkey is ignored).
/profile edits self; F5 and /who show the selected friend's last profile.
Run 309 reported success but uploaded 0 bytes for both aarch64 assets
(a 0.85s 'upload' of an 18MB file). The Pack step had just listed
18258736 bytes and sha256sum -c'd OK, so the failure is in the handoff,
not the build.
- publish-release.sh now fails loudly on a 0-byte asset instead of
publishing a release that silently breaks every download
- the workflow logs pwd, GITHUB_WORKSPACE, ls -l dist/ and the byte count
of each dist file immediately before publishing
Ruled out: the api_curl retry wrapper. Reproduced from inside a
node:20-bullseye container (curl 7.74, same as CI) — wrapper and plain
curl both uploaded 994 bytes correctly through Cloudflare.
The dispatched v0.1.1 build succeeded a third time (2m06s, valid ARM ELF)
and then died at the first API call inside the publish step with curl exit
92 (HTTP/2 stream error) — job containers reach Forgejo through Cloudflare,
where that is intermittent.
- all API calls go through api_curl: --http1.1 --retry 5
--retry-all-errors --retry-delay 3 --connect-timeout 20 --max-time 300
- HTTP/1.1 is the documented workaround for this instance's HTTP/2 resets
Job containers cannot reach http://forgejo:3000 (isolated GITEA-ACTIONS
network, name unresolvable, container IP unroutable — measured), so the
public hostname plus retries is the available fix.
Verified locally: create + idempotent re-run against a throwaway tag, both
clean; test release and tag deleted.
The dispatched build of v0.1.1 kept failing at publish with
'scripts/publish-release.sh: No such file or directory' — the workflow is
read from the dispatch ref (main) but the workspace was the v0.1.1 tag,
whose tree predates scripts/ and .forgejo/.
- second checkout of the workflow's own ref into .ci-tools; publish runs
.ci-tools/scripts/publish-release.sh (and .ci-tools/scripts/release-body.md)
- exclude ./.ci-tools from the build context tar
- target_commitish is now opt-in in publish-release.sh and only set for a
push event, so a re-publish can never move an existing tag
Verified locally end-to-end against a throwaway tag: create, idempotent
re-run (stale assets deleted + replaced), public download, sha256sum -c,
--version. Test release and tag deleted afterwards.
Two separate breakages from the first green-ish CI run:
1. tests/hs.rs asserted on a constant, which clippy rejects under
-D warnings (clippy::assertions_on_constants) because the compiler
folds the assert away. Compare through a runtime binding instead.
2. The release workflow built the aarch64 binary fine (13m02s, verified
ARM ELF) but the publish step died with:
/var/run/act/workflow/3: line 6: scripts/publish-release.sh: No such file or directory
run: steps execute with act's cwd, not the repo root, so relative
paths miss. Pin every run step with cd "${GITHUB_WORKSPACE}" (the
pattern the osint-dashboard workflow already relies on).
Verified locally before pushing: cargo test 41 passed, cargo clippy
--all-targets -- -D warnings clean.
rust:1.91-bookworm ships without clippy, so the clippy step died with
'cargo-clippy is not installed for the toolchain' after all 39 tests
passed. Also pushes the watcher fix (1=success 2=failure in Forgejo job
statuses, not action_run's numbering).
1=success 2=failure 3=cancelled 4=skipped 5=waiting 6=running 7=blocked,
terminal is 1..4. The first cut treated 2 as success and reported a failed
job as green.
The rust:1.87-bookworm job image failed every builder step with
'tor-*@0.46.0 requires rustc 1.91 ... either upgrade rustc or select
compatible dependency versions'. Cargo.toml declared rust-version 1.87,
which was simply wrong: cargo 1.87 could not even resolve the lockfile.
- rust-version 1.87 -> 1.91 (README requirement text too)
- RUST_IMAGE rust:1.87-bookworm -> rust:1.91-bookworm in ci and release
The GitHub workflows could never run on this instance: jobs asked for
ubuntu-24.04 runners (the Pi runner only registers the 'docker' label, so
every job sat in waiting), and release.yml published to github.com, which
is not a remote of this repo.
- .forgejo/workflows/ci.yml — cargo test + clippy on the 'docker' label,
Rust work in a rust:1.87-bookworm sibling container (aarch64 native)
- .forgejo/workflows/release.yml — on v*.*.* tags, build aarch64, strip,
sha256, publish to the Forgejo release; workflow_dispatch takes a tag
to re-publish
- scripts/publish-release.sh — idempotent create-or-update release +
asset upload via the Forgejo API (replaces same-named assets)
- scripts/build-release-local.sh — x86_64 path (no x86_64 runner exists)
- scripts/release-body.md — release notes template (@TAG@ substituted)
- README: install URLs -> Forgejo, accurate maintainer release procedure
- remove .github/workflows/*
- Hand-written wordmark banner (<=80 cols, auto-collapses to a compact
mark below banner width) plus onion glyph and themed screen headings
- Color-palette consts, stateful pane titles, highlighted selection row
(accent + bg), context-sensitive footer that always keeps '? help'
- Pane focus model (roster/chat/composer) with Tab/Shift-Tab, 1/2/3,
j/k + arrows, g/G, and a '?' help overlay; Esc closes any overlay
- New tests/tui_chrome.rs: banner widths, help overlay bindings, real
footer helper at 80 and 120 cols, 80x24 draw smoke test
- README keybinding table updated to match
Locked helper strings (fingerprint_mismatch_banner, rotate_screen_text,
wipe_screen_text, parse_slash) unchanged; UI-only (src/tui.rs).