# arca — v1.0.0 Plan

## Current State (v0.1.0)

Working proof of concept. FIDO2 unlock via cryptsetup with passphrase
fallback, privileged mount/unmount, config aliases with method
sequencing, init and status commands.

## M1: Idempotent mount/unmount

`mount` and `unmount` should be safe to run at any point in a device's
lifecycle without producing confusing errors.

### Files changed

- `cmd/mount.go`
- `cmd/unmount.go`
- `internal/udisks/client.go`

### Work

1. In `runMount`, after `FindDevice`, check if the device is already
   unlocked (`CleartextDevice` succeeds). If the cleartext device is
   already mounted, print the existing mount point and return success.
   If unlocked but not mounted, skip unlock and go straight to mount.

2. In `runUnmount`, handle each failure case:
   - `CleartextDevice` fails (already locked): print "already locked"
     and return success.
   - `Unmount` fails because not mounted: proceed to lock anyway.
   - `Lock` fails because already locked: return success.

3. Add `client.IsMounted(dev)` helper that returns `(mountpoint, bool)`
   to reduce duplicated mount-point checking logic.

---

## M2: Error messages

Replace generic D-Bus errors with actionable messages.

### Files changed

- `internal/udisks/client.go`
- `cmd/mount.go`, `cmd/unmount.go`, `cmd/init.go`, `cmd/status.go`

### Work

1. Wrap the `dbus.SystemBus()` error in `NewClient` to detect
   "connection refused" or "no such file" and print:
   `"cannot connect to udisks2 — is the udisks2 service running?"`

2. In `FindDevice`, when no device matches, include what was searched
   and suggest `arca status` or `arca init`:
   `"device /dev/sda1 not found (run 'arca status' to list devices)"`

3. In the unlock sequencer, prefix each method error with context:
   `"fido2: cryptsetup open --token-only: exit status 5 (is the FIDO2 key plugged in?)"`

---

## M3: Unit tests

Cover pure logic that doesn't need D-Bus or real devices.

### Files changed

- `internal/config/config_test.go` (new)
- `internal/cryptsetup/cryptsetup_test.go` (new)

### Work

1. **config_test.go** — test cases:
   - `ResolveDevice` with exact alias match
   - `ResolveDevice` with device path match (`/dev/sda1` -> `sda1`)
   - `ResolveDevice` with unknown name returns default methods
   - `ResolveDevice` with empty methods list defaults to `[passphrase]`
   - `AliasFor` finds alias by UUID
   - `AliasFor` returns "" for unknown UUID
   - `Load` with nonexistent file returns empty config
   - `Load` with valid YAML parses correctly

2. **cryptsetup_test.go** — test cases:
   - `MapperName("/dev/sda1")` == `"arca-sda1"`
   - `MapperName("/dev/nvme0n1p2")` == `"arca-nvme0n1p2"`
   - `hasTokenPlugins` with a temp dir containing a matching .so
   - `hasTokenPlugins` with an empty temp dir

---

## M4: CLI polish

Small usability improvements.

### Files changed

- `cmd/root.go`
- `cmd/mount.go`
- `cmd/init.go`
- `main.go`
- `.gitignore` (new)

### Work

1. Add `var version = "dev"` to `main.go`. Set `rootCmd.Version` in
   `cmd/root.go`. Build with `-ldflags "-X main.version=..."` in
   `flake.nix`.

2. Add `--mountpoint` / `-m` flag to `mount` subcommand. When set,
   pass it to `cryptsetup.Mount` (privileged path) or log a warning
   that udisks2 doesn't support custom mount points.

3. In `init`, use first 8 chars of UUID as alias instead of device
   path basename. Example: `b8b2f8e3` instead of `sda1`. UUIDs are
   stable across boots; device paths are not.

4. Create `.gitignore` containing `/arca`.

---

## M5: Documentation and packaging

Make the project installable and the README trustworthy.

### Files changed

- `README.md`
- `flake.nix`

### Work

1. Update `README.md`:
   - Replace placeholder UUIDs with realistic examples from actual
     tested usage.
   - Add "NixOS notes" section documenting the `LD_LIBRARY_PATH`
     requirement for FIDO2/TPM2 and why `--external-tokens-path`
     doesn't work.
   - Add "Troubleshooting" section: no FIDO2 token enrolled, udisks2
     not running, permission denied on mount.
   - Document `init` subcommand.

2. Verify `flake.nix`:
   - Run `nix build` and confirm it produces a working binary.
   - Add `-ldflags` for version injection from `self.rev` or a
     `version` variable.
   - Test the flake output: `./result/bin/arca --version`.

3. Tag `v1.0.0`.

---

## Non-goals for v1.0.0

- `--dry-run` flag
- `--json` output for `status`
- udev auto-mount on plug
- Keyfile creation/management
- Multiple config files or config includes
