Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6cb60ba
docs(readme): note unconfirmed Linux oversized-cursor issue (#6)
maosuarez Aug 4, 2026
b28f310
fix(window-chrome): show minimize/close controls on Linux
maosuarez Aug 4, 2026
cd36da2
fix(mcp): unify environment-scope param name on environment_id (issue…
maosuarez Aug 4, 2026
01aef84
fix(db,project,api): case-insensitive uniqueness for environment/proj…
maosuarez Aug 4, 2026
98284e5
feat(wsl,project): add WSL bridge for the environment .env path picke…
maosuarez Aug 4, 2026
8cbee09
fix(api,project,cli,mcp): gate .env write targets against silent clob…
maosuarez Aug 4, 2026
ba22dff
test(api,db,project,vault,share,cli,mcp): add shared test harness + c…
maosuarez Aug 4, 2026
bd78bd6
fix(db,project,api): fix non-ASCII dedup fold mismatch, report durabi…
maosuarez Aug 4, 2026
34bf3d7
merge: issue #11 test harness + coverage baseline
maosuarez Aug 5, 2026
36db76f
merge: issue #12 case-insensitive environment/project name uniqueness
maosuarez Aug 5, 2026
db92f5e
merge: issue #5 Linux window controls
maosuarez Aug 5, 2026
c6c2cf0
merge: issue #6 oversized cursor docs
maosuarez Aug 5, 2026
6c54561
merge: issue #3 WSL bridge for environment .env path picker
maosuarez Aug 5, 2026
09522c1
merge: issue #10 unify MCP environment-scope param on environment_id
maosuarez Aug 5, 2026
7d96152
merge: issue #8 gate .env write targets against silent clobbering
maosuarez Aug 5, 2026
350f8ee
ci(test): run the integration suites under tests/, not just --lib --bins
maosuarez Aug 5, 2026
b8b5aae
merge: tier-1 CI fix into tier 2
maosuarez Aug 5, 2026
77a5160
Merge remote-tracking branch 'origin/main' into batch/tier2-independe…
maosuarez Aug 6, 2026
30defcf
fix(mcp): restore content dropped by faulty conflict-resolution script
maosuarez Aug 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ src-tauri/target/

# Claude Code local configuration
.claude/
CLAUDE.local.md

# Environment
.env
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [1.0.2] - 2026-08-04

### Changed

- **MCP: unified the environment-scope parameter name on `environment_id`** (issue #10). `crypt_env_inject_environment` and `crypt_env_generate_example_env` were the only two MCP tools naming the environment identifier `id` instead of `environment_id`, matching every other environment-scoped tool. This let an LLM caller that inferred the parameter name from the majority pass `environment_id` to these two tools and have it silently ignored β€” in the case where `project`+`environment` were also present, the resolver would fall through and inject a *different* environment's full decrypted variable set with no error. Both schemas now advertise `environment_id` with the canonical description used by every other scoped tool; the ambiguity error in `crypt_env_inject_env_by_name` now names `environment_id` instead of `id` as well.
- The bare `id` key is accepted as an unadvertised, deprecated alias for the whole 1.0.x line (`// DEPRECATED(remove in 1.1.0): environment 'id' alias, issue #10`). Calls using `id` still succeed, but the response text appends a fixed deprecation notice so the model is told to switch. Removed in 1.1.0.
- New in-crate tests (`src-tauri/src/bin/crypt-env-mcp.rs`, `#[cfg(test)] mod tests`) assert every environment-scoped tool declares `environment_id` and not a bare `id`, that the five item/category/workspace tools keep their bare `id`, and that the resolver prefers `environment_id` over the alias.
- `docs/reference.md` updated to drop the "inconsistent naming" note and describe the resolved state + deprecation window.

## [1.0.1] - 2026-07-28

### Fixed
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ crypt-env workspace inject "my-node-project"

Via UI: Settings β†’ Workspaces β†’ [select workspace] β†’ **INJECT TO PATH**

### WSL paths (Windows only)

If your project's source lives inside a WSL2 distro, its `.env` file is reachable from Windows at `\\wsl.localhost\<distro>\...` β€” the environment path picker has a **WSL** button (next to the regular browse button) that lists your installed distros and opens the file dialog right there, so you don't have to type the UNC path or know your distro's exact registered name. Two things to know:

- Browsing (or injecting to) a WSL path **starts the distro** if it's stopped β€” this can take a few seconds and uses memory, the same as running `wsl` from a terminal would.
- A stopped distro must be running for **inject** to succeed too β€” if the distro isn't up, the write fails with an error instead of silently succeeding.

---

## πŸ’» Interactive TUI
Expand Down Expand Up @@ -169,6 +176,8 @@ Useful for SSH sessions, CI/CD scripts, or environments where a GUI is unavailab
- **macOS**: Xcode Command Line Tools (`xcode-select --install`)
- **Linux**: `libwebkit2gtk-4.1-dev`, `libgtk-3-dev`, `libayatana-appindicator3-dev`, `librsvg2-dev`

> **Known issue (Linux):** the mouse cursor may render oversized inside the app window on some Linux setups. Root cause is not yet confirmed β€” diagnosis is blocked on a native X11/Wayland desktop session (WSLg is not a valid repro/verification environment for this). No workaround is documented yet. Track status in [issue #6](https://github.com/maosuarez/crypt-env/issues/6).

### Install & Run

```bash
Expand Down
30 changes: 30 additions & 0 deletions context.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ crypt-env/
β”‚ β”‚ β”œβ”€β”€ crypto/mod.rs # Argon2id KDF + AES-256-GCM encrypt/decrypt
β”‚ β”‚ β”œβ”€β”€ vault/mod.rs # VaultState, Tauri commands for vault management
β”‚ β”‚ β”œβ”€β”€ project/mod.rs # Project/environment business logic (shared by Tauri + HTTP API)
β”‚ β”‚ β”œβ”€β”€ wsl/mod.rs # WSL bridge (Phase 1): distro discovery + UNC seed dir for the env path picker
β”‚ β”‚ β”œβ”€β”€ api/mod.rs # Axum server on 127.0.0.1:47821, dual token auth
β”‚ β”‚ β”œβ”€β”€ share/mod.rs # Secure secret sharing (LAN bridge + encrypted packages)
β”‚ β”‚ β”œβ”€β”€ share/relay.rs # Internet relay sharing (Supabase-based)
Expand All @@ -54,6 +55,7 @@ crypt-env/
**New Tauri Commands** (Session 4+):
- Projects/Environments: `project_list`, `project_save`, `project_delete`, `project_preview_delete`, `environment_save`, `environment_delete`, `environment_inject`, `vault_create_project_item`, `vault_set_item_global`, `vault_get_item_owners`
- Internet Relay: `share_relay_send`, `share_relay_receive`
- WSL bridge (issue #3, Phase 1, Windows-only affordance): `wsl_list_distros() β†’ Vec<String>` (always `Ok`, empty on non-Windows/no-WSL), `wsl_distro_home(distro: String) β†’ Result<String, String>` (seed dir for the env path picker). `project_pick_env_path` gained an optional `start_dir: Option<String>` argument to seed the native dialog at that directory

## Vault Item Types
1. **Secret / API Key**: name, encrypted value, category, notes. Export as `.env` / `export` / `$env:`
Expand Down Expand Up @@ -872,6 +874,34 @@ CREATE TABLE settings (

---

### 16. WSL Bridge for the Environment Path Picker (Issue #3, Phase 1)
**Context**: Windows developers who keep their project source inside a WSL2 distro (this repo itself, per `CLAUDE.local.md`) can already attach a WSL `.env` to an environment by typing its `\\wsl.localhost\<distro>\...` UNC path into the existing manual path input β€” but nothing in the UI suggests this is possible, the user must know their distro's exact *registered* name, and a typo or a stopped distro previously surfaced as a confusing later failure. This is discoverability and pre-flight validation, not new capability β€” see the plan's own accounting in `docs/plans/issue-3-wsl-bridge-env-paths.md` Β§2.

**Decision**: Add a "Browse WSL" affordance next to the existing browse button in the environment editor's PATHS block, backed by two new Windows-only Tauri commands in a dedicated `wsl` module. Phase 2 (a CLI running inside WSL talking back to the vault) is explicitly deferred, uncommitted work (plan Β§6).

1. **New module** `src-tauri/src/wsl/mod.rs` β€” decoupled from `db`/`vault`/`api`/`project`; only the frontend composes it with `project_pick_env_path`.
- `parse_distro_list(&[u8]) -> Vec<String>` β€” pure. Decodes `wsl.exe --list --quiet` stdout: UTF-8 BOM, UTF-16LE BOM, BOM-less UTF-16LE (heuristic: every second byte of the first 32 is `0x00`), or UTF-8 lossy fallback. 11 unit tests in-file.
- `unc_root(distro)` / `unc_root_legacy(distro)` β€” pure, return `\\wsl.localhost\<distro>\` / `\\wsl$\<distro>\`.
- `wsl_list_distros() β†’ Result<Vec<String>, String>` (Tauri command) β€” spawns `wsl.exe --list --quiet` via `tokio::process::Command` + `kill_on_drop` under a 10 s `tokio::time::timeout`. Returns `Ok(vec![])` β€” never `Err` β€” when `wsl.exe` is absent, WSL has zero distros, or the target isn't Windows; `Err` only on timeout or a genuine non-`NotFound` spawn error.
- `wsl_distro_home(distro) β†’ Result<String, String>` (Tauri command) β€” validates the argument (no `\`, `/`, `..`, NUL), probes `\\wsl.localhost\<distro>\home\` then falls back once to `\\wsl$\<distro>\home\` (each probe `spawn_blocking` + 10 s timeout, since a UNC probe against a cold distro blocks). Descends into the single child directory if `read_dir` finds exactly one, else returns `...\home\`. Touching the UNC path starts a stopped distro β€” documented in the button's tooltip.
2. **`project_pick_env_path`** (`src-tauri/src/project/mod.rs`) gained an optional `start_dir: Option<String>` argument; when given, calls `.set_directory()` on the `rfd::FileDialog` builder before `pick_file()`. The other four `rfd` call sites (`project_export`, `project_import`, the two in `share_commands.rs`) are untouched.
3. **Frontend** (`src/components/ProjectManager.tsx`): `isWindows` detected via `@tauri-apps/plugin-os` `platform()`, mirroring `WindowChrome.tsx`. `wsl_list_distros` fetched once when `isWindows` becomes true, held in local component state (not Zustand, not TanStack Query β€” ephemeral machine state). A "WSL" button (terminal icon) renders in the PATHS row only when `isWindows && wslDistros.length > 0`: one distro acts directly, several open a small inline dropdown (same panel styling as the existing tag filter). Click β†’ `wsl_distro_home(distro)` β†’ `project_pick_env_path({ startDir })`, reusing the same picked-path handling as the plain browse button (`applyPickedEnvPath`, extracted from the old `handlePickEnvPath`) β†’ failure shows a toast and falls back to the manual input.
4. **Data-loss gate landed alongside this** (plan Β§3.6, cross-cutting with issues #7/#8): `inject_environment`'s read of each target path (`src-tauri/src/project/mod.rs`) previously did `std::fs::read_to_string(path).unwrap_or_default()`, treating *any* read failure β€” including a stopped WSL distro's dead 9p mount β€” as "file is empty," then silently overwriting it with only this environment's keys. Changed to a `match e.kind()` guard: `ErrorKind::NotFound` still starts from empty (new file); every other error kind aborts the write with `Err`. This is the single most likely real-world failure mode of attaching a WSL path, and the plan declared Phase 1 not closeable without it.

**Rationale**:
- `--list --quiet` over the issue-specified `--list --verbose`: verbose output is column-aligned, carries a localized `Running`/`Stopped`/default-marker, and breaks on non-English Windows; quiet emits one bare name per line and needs no state (selecting a distro starts it anyway).
- `tokio::process::Command` (not `spawn_blocking` + `std::process::Command`, the pattern already used for `rfd` in this file) because a `timeout` around a blocking-pool `JoinHandle` doesn't cancel the blocked thread β€” it leaks a pool slot for as long as `wsl.exe` hangs. `tokio::process` cancels and kills the child on drop.
- No new Cargo dependency: hand-rolled UTF-16LE decode (~10 lines) instead of `encoding_rs`, since exactly two known encodings from one known producer don't justify a crate; `tauri-plugin-shell` (listed in this file's own stack-setup checklist, never actually added β€” see `src-tauri/Cargo.toml`) is deliberately not added either, since it would let the webview spawn processes for no gain when Rust can already do so natively.
- `\\wsl.localhost\` primary / `\\wsl$\` fallback (not build-number sniffing): two cheap filesystem probes beat silently guessing wrong on older Windows builds.

**Consequences**:
- Windows-only feature; the command surface still registers on all targets (honest `Ok(vec![])` elsewhere) so there is one `invoke_handler!` list, not a per-platform one.
- Clicking "Browse WSL" can cold-boot a stopped WSL VM (several seconds, memory cost) β€” inherent to the UNC bridge, stated in the tooltip, not eagerly triggered (only on explicit click).
- `docker-desktop`/`docker-desktop-data` distros are not filtered out of the list (would hardcode a vendor's naming into the parser); users recognize their own distros.
- Requires manual verification on real Windows + WSL2 hardware (checklist M1–M10 in the plan) β€” not automatable in CI, since CI has no WSL. Automated coverage is limited to the pure `parse_distro_list`/`unc_root` functions and the `inject_environment` read-guard (both covered by `cargo test`).

---

## Security Status (post-review 2026-04-24)

A **comprehensive security review** was performed that identified **19 findings** (7 HIGH, 8 MEDIUM, 4 LOW). **All findings have been addressed**.
Expand Down
Loading
Loading