Files
Bitsy 876122ff98 Audit fixes: security hardening, index correctness, performance, UX
Security
- Elevated copies: root only reads the original; the copy is written by
  the user (sudo -u tee), so root never chowns / chmods a user-controlled
  path. Staging folder forced to 0700.
- Service logs moved from fixed /tmp names to /Library/Logs/Allofit
  (root-owned) and ~/Library/Logs/Allofit; Diagnostics reveals instead of
  opening the log.
- Index files are owner-only (0600; the root daemon's belongs to the
  installing user), set on the temp file before an atomic rename.
- Root install passes the plist inline (base64, plutil -lint) instead of a
  user-writable temp file; the binary comes from Bundle.main.
- Cache loader caps and checks the declared payload size; each save uses
  its own temp file.
- Release action pinned to a commit; non-system LC_RPATHs stripped.

Correctness
- Move to Trash removes the files from the index (the watcher ignores the
  app's own operations) and registers Undo (Put Back); trashed folders are
  matched with the original URLs; failures are shown.
- The saved event id stays below pending subtree walks (GUI and service).
- Roots / exclusions changes restart the watcher from the snapshot's id.
- Case-only renames no longer leave a ghost entry.
- Service mode is saved only after a successful install; a saved but
  missing service falls back to the in-process indexer.

Performance
- New folders are merged without the O(n) removal pass.
- Size / date sorts use a compact key array (539 -> 47 ms for 630k).
- Selection, preview and actions use the selected records directly.
- Service saves at most every 15 s; reader reloads pause while hidden and
  are deferred instead of dropped; window close saves only when dirty.

Usability
- Results appear during the first index; empty-list explanations.
- Down arrow moves to the results, Up on the first row back; history on
  Up / Option-Up / Option-Down.
- Search syntax popover and Help menu; shortcuts shown in the context menu;
  confirmations for Clear Cache and Uninstall; privacy usage strings;
  Group Containers excluded by default; wording, VoiceOver labels, plural.
2026-10-02 14:44:49 +02:00

78 lines
3.5 KiB
Markdown

# Allofit
Fast file-name search for macOS, inspired by [voidtools Everything](https://www.voidtools.com/).
Keeps an in-memory index of file names + metadata, updates in real time via FSEvents, and filters instantly as you type. Optional background service so the index stays warm between launches.
## Install
Grab the latest `.dmg` from [Releases](https://github.com/bitsycore/Allofit/releases). Open it, drag `Allofit` onto `Applications`.
First launch will be blocked by Gatekeeper (ad-hoc signed). Right-click → **Open**, or:
```bash
xattr -dr com.apple.quarantine /Applications/Allofit.app
```
Requires macOS 15 (Sequoia) or newer.
## Search
Same syntax as Everything:
| Type… | …to match |
|---|---|
| `report` | any name containing "report" (case-insensitive) |
| `annual report` | names containing "annual" **and** "report" |
| `Start*.pdf` | starts with "Start", ends with ".pdf" |
| `IMG_????.heic` | "IMG_" + exactly 4 chars + ".heic" |
| `*.png \| *.jpg` | OR - png or jpg (OR binds tighter than the space AND) |
| `report !draft` | NOT - names with "report" but without "draft" |
| `"my file"` | quotes keep spaces inside one term |
| `ext:pdf;docx` | by extension |
| `file:` / `folder:` | files only / folders only, alone or as a prefix (`folder:build`) |
| `src/main` | a term with `/` matches against the full path |
| `"some/folder/**/path" IMG_????.heic` | path wildcards: `*` stays inside one folder name, `**` spans any number of folders (zero too) |
| `photos/**/` | a trailing `/` means anything inside that folder |
Every match is listed, in the order of the column you click; the filter menu next to the search box narrows to one kind of item, and the ? button shows this syntax in the app. Hidden files and folders (dot-files, `~/Library` when hidden) are not indexed.
## Shortcuts
| Key | Action |
|---|---|
| ⌥Space | Show / hide Allofit from any app (changeable in Settings) |
| ⌘F | Focus search |
| ↑ ↓ (in the search field) | Cycle search history |
| Return | Open the selection (or reveal it, see Settings) |
| ⌘Return | Reveal in Finder |
| Space / ⌘Y | Quick Look |
| ⌘C / ⌥⌘C | Copy the files / their paths |
| ⌘⌫ | Move to Trash |
| Hold ⌥ | Freeze the list: no background updates until released |
| ⇧⌘R | Rebuild the index |
| ⌘, | Settings |
Hovering a cut-off name or path for half a second shows it in full. In Finder, right-click a folder → Quick Actions → **Search in Allofit** to search inside it.
## Background service (optional)
**Settings → Advanced → Service** installs a LaunchAgent (user) or LaunchDaemon (root) that keeps the index updated even when the app is closed. The chosen mode only takes effect once Install succeeds. Service logs go to `~/Library/Logs/Allofit/` (user) or `/Library/Logs/Allofit/` (root).
Root daemon mode needs **Full Disk Access** granted to its binary in **System Settings → Privacy & Security**, otherwise it won't see new files in `~/Documents`, `~/Desktop`, `~/Downloads`. **Settings → Advanced → Diagnostics** shows the exact path to add. The root daemon's index is readable only by root and the user who installed it.
## Build from source
```bash
./scripts/build-app.sh # produces outputs/Allofit-0.0.0.app
./scripts/build-dmg.sh # produces outputs/Allofit-0.0.0.dmg
```
(The version is stamped into both the filename and the bundle's Info.plist. Pass `--version 1.2.3` or set `ALLOFIT_VERSION=1.2.3` to override.)
`./scripts/clean.sh` wipes services, caches, prefs, and build artifacts (including `outputs/`) for a fresh start.
## License
[MIT](LICENSE)