Addresses #44 (closed after the v0.8.3 release builds are green).
Three independent defects in one shared tool-resolution path: bundled tools were
looked up at the wrong path (so bundled fallback never worked, for PostgreSQL
too), macOS GUI apps inherit launchd's minimal PATH, and only mariadb-dump/mariadb
were probed — never mysqldump/mysql, which is what brew install mysql-client
installs. Both engines now resolve through one headless, cached, timed resolver,
plus a non-blocking client/server version warning.
Also fixes passing MySQL-only CLI options to the bundled MariaDB client, and
carries the tool-page UI polish and the requested #40 icon revert.
Fixes#41.
MySQL columns now decode by their declared type instead of a blind try_get chain,
so every non-string type (integers, DECIMAL, BIT, YEAR, DATE/TIME/DATETIME/TIMESTAMP)
renders its value instead of NULL. MySQL restore runs in-process over the sqlx driver
(no bundled MariaDB client, so MySQL 8 caching_sha2_password works) and the Clean toggle
is implemented. .sql files can be opened with Gridline from the OS file manager.
Verified: cargo test --lib 442 passed / 0 failed; bun run test 133 files / 1151 tests
exit 0; tsc clean; both live MySQL 8 integration tests pass with the bundled CLI hidden.
* fix(keychain): repair stale macOS keychain ACLs before reads
macOS records the trusted application of a legacy keychain item as the
executable *path* for an ad-hoc (dev/linker-signed) build, and as a code
requirement + cdhash partition list for a signed bundle. When the path
changes — the monorepo move (`src-tauri/` -> `desktop/src-tauri/`), a
reinstalled app, running the installed app after a dev build — the ACL no
longer admits the running binary, so macOS demands the keychain password on
*every* access: once per secret, every time, with no "Always Allow".
The pin only ran after writes, and writing to an inaccessible item needs the
same authorization, so the ACL was never repaired.
- keychain_acl::is_item_trusted() answers "am I already in this item's ACL?"
with a metadata-only, prompt-free lookup (SecKeychainItemCopyAccess +
SecAccessCopyACLList + comparison against this process's own
SecTrustedApplication representation; a signed bundle's `cdhash:` partition
lists count as trusted and are left alone).
- ensure_item_acl() now no-ops when already trusted (no duplicate ACL entries,
no redundant authorization) and otherwise pins exactly as before.
- New before_read() hook, called from every keychain read path (DB password,
SSH password, SSH passphrase — commands and internal helpers), so a stale
item costs one authorization and is silent from then on.
- ItemSearchOptions reports "nothing matched" as errSecItemNotFound, which is
now handled as "no item" instead of a lookup error — previously every
connection without a stored password logged a spurious failure.
Verified: cargo test --lib (408 passed) plus the ignored live test
(`cargo test --lib -- --ignored keychain_acl`), which asserts a fresh item is
already trusted, repairs are idempotent and the secret still reads back. That
test now finishes in 0.25s instead of ~8s because the redundant
SecKeychainItemSetAccess is gone.
* chore(release): bump 0.8.1 and document GitHub-only releases
Version 0.8.0 -> 0.8.1 across every synced location: desktop/package.json,
desktop/src-tauri/Cargo.toml (+ Cargo.lock gridline entry), tauri.conf.json,
www/src/pages/index.astro, www/public/landing.js, both README download tables
(+ the MAINTENANCE note), and the version/docs-coverage/bundle-config tests.
Adds the 0.8.1 changelog entry (site + README Recent Changes).
AGENTS.md: releases are GitHub-only — tag `prod` and `git push github vN.M.N`
(origin/Gitea is a just-in-case mirror; never push branches, PRs or tags
there), and the keychain row documents the self-healing ACL repair.
Connecting to a managed provider (Neon in the report) failed with
"connection is insecure (try using `sslmode=require`)" because the TLS mode
never reached the backend:
- ConnectionFormData carried no ssl_* fields and both submit handlers
(NewConnectionScreen.buildPayload, EditConnectionModal save + test) omitted
them, so the SSH/SSL tab's mode was silently dropped before IPC.
- SslFields wrote ssl_ca_cert / ssl_client_cert / ssl_client_key — names the
Rust ConnectionInput ignores (it expects ssl_ca_path / ssl_cert_path /
ssl_key_path).
- parseConnectionString ignored query params, so a pasted Neon/Supabase URL's
?sslmode=require was discarded.
Pasting a URI now fills every setting it can (sslmode / ssl-mode, including
MySQL's REQUIRED and VERIFY_IDENTITY spellings) and offers a derived label
(database name, else host/file stem) as a placeholder that Test/Save uses when
the field is left blank. Managed presets pre-select their TLS mode (Supabase /
Neon → Require, PlanetScale → Verify Full).
Backend safety net: db::tls::resolve_ssl_mode defaults *.neon.tech /
*.supabase.co / *.psdb.cloud to `require` when a connection has no ssl_mode, so
connections saved before this fix keep working; an explicit mode (including
disable) always wins.
Tests: +15 frontend (parser, form payload, SSL field names, edit modal),
+4 Rust (resolve_ssl_mode / tls_decision_for).