{"id":51063636,"url":"https://github.com/firstdorsal/nailbite","last_synced_at":"2026-06-23T04:30:48.984Z","repository":{"id":358708514,"uuid":"1242718787","full_name":"firstdorsal/nailbite","owner":"firstdorsal","description":"A quiet local companion that gently notices when your hands wander to your face — BFRB awareness, on-device, no cloud.","archived":false,"fork":false,"pushed_at":"2026-05-18T19:01:23.000Z","size":1063,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-18T19:37:00.419Z","etag":null,"topics":["bfrb","computer-vision","habit-tracker","linux","local-first","mental-health","onnx","privacy","react","rust","tauri","webcam"],"latest_commit_sha":null,"homepage":"https://github.com/firstdorsal/nailbite","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/firstdorsal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T17:29:52.000Z","updated_at":"2026-05-18T19:01:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/firstdorsal/nailbite","commit_stats":null,"previous_names":["firstdorsal/nailbite"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/firstdorsal/nailbite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firstdorsal%2Fnailbite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firstdorsal%2Fnailbite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firstdorsal%2Fnailbite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firstdorsal%2Fnailbite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/firstdorsal","download_url":"https://codeload.github.com/firstdorsal/nailbite/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firstdorsal%2Fnailbite/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34675970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-23T02:00:07.161Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bfrb","computer-vision","habit-tracker","linux","local-first","mental-health","onnx","privacy","react","rust","tauri","webcam"],"created_at":"2026-06-23T04:30:48.411Z","updated_at":"2026-06-23T04:30:48.977Z","avatar_url":"https://github.com/firstdorsal.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# n**AI**lbite\n\n**A quiet companion that notices when your hands wander to your face — and gently lets you know.**\n\nIf you've ever caught yourself mid-bite and thought \"oh, again\" — n**AI**lbite is built for that\nmoment. It watches your webcam, recognises a small set of body-focused repetitive behaviours\n(BFRBs) — currently nail biting and nail picking — and surfaces a soft alert when it sees one.\nThat's it. No streaks, no scolding, no leaderboards. You decide what to do with the nudge.\n\nEverything happens on your own machine. Nothing is uploaded.\n\n## Why this exists\n\nI needed something that would stop me from biting my nails while I'm sitting at\nmy desk. By the time I notice my hand is at my mouth, it's already too late —\nthe whole thing happens below the level of conscious attention. Nothing I tried\n(bitter polish, hair ties, sticky notes on the monitor) survived a deep work\nsession.\n\nWhat did work was having someone sitting next to me who would say \"hey, you're\ndoing it again.\" That's the role this tool is built to fill: a patient external\nobserver that never gets tired, never judges, never makes a thing of it — just\nquietly says \"hey\" so I can put my hand back down and carry on.\n\nIt's a personal tool, shared in case it's useful to someone else with the same\nproblem. It is not a medical device, and it doesn't try to be one.\n\n## What it does\n\n- **Notices the gesture.** Hand/face/pose tracking with on-device ONNX models, running at a\n  modest ~8 FPS so it's friendly to your CPU.\n- **Waits for sustained contact** before alerting (a brief brush of the lip won't trigger it).\n- **Plays a soft sound and a desktop notification** so you can choose to redirect the gesture.\n- **Lets you label each alert** \"correct\" or \"false positive\" with one click, so the thresholds\n  can be tuned to *your* hands over time.\n- **Keeps a short clip and the landmark trace** of each event in `~/.local/share/nailbite/` so\n  you can review what happened if you want to — and delete it if you don't.\n- **Runs entirely locally.** No telemetry, no cloud, no account. The only network feature is\n  an optional webhook that ships off by default.\n\n## What it doesn't do (yet)\n\n- Hair pulling, skin picking, lip biting — these have detector stubs but aren't enabled. The\n  generic pipeline is there; per-behaviour signals need tuning before they're trustworthy.\n- macOS / Windows. Linux x86_64 only at the moment; the camera and tray code are behind\n  platform traits so the door is open.\n- \"Coaching\" or gamification. By design — a counter that nudges you toward a number can turn\n  into another source of shame, which is the opposite of what helps.\n\n## Screenshot\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/images/hero.png\" alt=\"nailbite live preview with hand, face mesh, and pose torso landmarks overlaid; tray status reads 'Monitoring'\" width=\"780\"\u003e\n\u003c/p\u003e\n\nThe live preview during monitoring. Hand landmarks (green = left, orange = right)\nride on top of the face mesh and pose torso so you can see exactly what the\ndetector is seeing — useful when you're tuning thresholds, and reassuring when\nyou want to know what is and isn't being tracked.\n\n## Install\n\nPre-built binaries are on the [Releases page](../../releases/latest) for every tagged\nversion. The release notes include copy-paste install commands with exact URLs.\n\nPick whichever format you prefer; both contain the same app.\n\n### AppImage (works on most distros, no install needed)\n\nDownload the AppImage for your hardware, then:\n\n```bash\nchmod +x nailbite_*_amd64-cpu.AppImage\n./nailbite_*_amd64-cpu.AppImage\n```\n\nOn **NixOS**:\n\n```bash\nnix-shell -p appimage-run --run './nailbite_*_amd64-cpu.AppImage'\n```\n\nOn **Ubuntu 24.04** the AppImage needs the legacy FUSE shim:\n\n```bash\nsudo apt install libfuse2t64\n```\n\n### Debian / Ubuntu (.deb)\n\n```bash\nsudo apt install ./nailbite_*_amd64-cpu.deb\n```\n\n### GPU builds\n\nCPU is fine for most people — the detection pipeline is light. If you have a discrete GPU and\nwant the headroom, replace `cpu` in the filename with the backend matching your hardware:\n\n| Backend | Filename suffix | Runtime requirement              |\n|---------|-----------------|----------------------------------|\n| CPU     | `-cpu`          | none                             |\n| NVIDIA CUDA     | `-cuda`     | NVIDIA driver 525+, CUDA 12.x    |\n| NVIDIA TensorRT | `-tensorrt` | NVIDIA driver 525+, CUDA 12.x    |\n| AMD ROCm        | `-rocm`     | AMD ROCm 6.0+                    |\n| AMD MIGraphX    | `-migraphx` | AMD ROCm 6.0+                    |\n\n### Verifying downloads\n\nEvery release ships a `SHA256SUMS.txt`. Drop it next to the binary you downloaded and run:\n\n```bash\nsha256sum -c SHA256SUMS.txt --ignore-missing\n```\n\n### Requirements\n\n- Linux x86_64 (NixOS, Arch, Debian 12+, Ubuntu 22.04+ tested)\n- A V4L2-compatible webcam (most USB and laptop cameras work out of the box)\n- Membership of the `video` group (most distros add the primary user automatically)\n\n## Using it\n\nThe first launch downloads a small set of ONNX models (~50 MB) and caches them locally. After\nthat, n**AI**lbite lives in your system tray:\n\n- 🟢 monitoring\n- 🟡 paused\n- 🔴 BFRB currently detected\n- ⚫ no one in frame\n- ⚪ offline\n\nA few keys you'll probably want:\n\n| Key | Action |\n|-----|--------|\n| F9  | Dismiss the latest alert as a false positive (helps the detector learn) |\n| F10 | Mark a missed event (false negative) |\n| F11 | Pause / resume detection |\n\nWhen an alert fires you get an inline \"Correct / False positive\" choice on the notification\nitself. The history page lets you scrub through past events with the landmarks overlaid, and\nthe insights page sweeps thresholds against your labels to suggest tuning.\n\n## Configuration\n\nThe single source of truth is `config.yaml`. The defaults are tuned to be conservative\n(prefers missing a real event to firing a false one), and the camera bias is set up so your\nface stays well-lit even with a bright window behind you.\n\nA small taste:\n\n```yaml\ncamera:\n  inference_fps: 8\n  controls:\n    gamma_reset: true               # reset gamma to driver default at startup\n    auto_exposure: true\n    exposure_auto_priority: true    # let exposure stretch in dim / backlit scenes\n    auto_white_balance: true\n    auto_gain: true\n    backlight_compensation_max: false   # opt-in for backlit scenes only\n    brightness_fraction: null       # null = keep camera default\n    contrast_fraction: null\n  sources:\n    - id: main\n      device: /dev/video0\n      role: primary\n\ndetection:\n  behaviors:\n    nail_biting:\n      enabled: true\n      proximity_threshold: 0.35\n      min_sustained_ms: 1000        # 1s of dwell before alerting\n\nort:\n  gpu:\n    preference: auto                # auto / disabled / required\n```\n\nSee `docs/configuration.md` for the full reference.\n\n## How it works under the hood\n\n1. **Camera** → frames pulled from V4L2.\n2. **Detection + rotation** → palm and face SSD models emit bounding boxes *and* keypoints\n   (palm: wrist + finger MCPs; face: eye line). The pipeline rotates each crop so the hand's\n   fingers point up and the face's eyes are horizontal before the landmark model sees it —\n   that's the distribution those models were trained on, and the single biggest factor in\n   how stable the predictions look frame-to-frame.\n3. **Landmarks** → hand (21 keypoints), face (468), pose (33) run on the rotated crops; the\n   outputs are inverse-rotated back into image space for visualisation and the behaviour\n   detectors.\n4. **Behaviour analysis** → temporal smoothing + per-hand explanations decide whether the\n   geometry actually looks like nail biting or picking, rather than typing, eating, scratching\n   an itch, or resting on a chin.\n5. **Alert + label** → soft sound, desktop notification with inline labels, snapshot saved to\n   your local history.\n\nMore detail in `docs/architecture.md` and `docs/detection.md`.\n\n## Privacy\n\nThis is software that points a camera at you. Privacy isn't a bullet point — it's the whole\nshape of the thing.\n\n- No video frames or images ever leave your machine.\n- No cloud, no account, no telemetry.\n- Event clips and stats live under `~/.local/share/nailbite/`. They are yours; delete them\n  whenever you like.\n- The only optional network feature is a webhook (disabled by default) so you can route alerts\n  into your own systems if you want.\n\n## Research direction \u0026 looking for collaborators\n\nThe current detector is **geometric**: it runs off-the-shelf hand, face, and\npose landmark models on each frame and then asks rule-based questions of the\nlandmark coordinates — \"are any fingertips close to the outer-lip contour for\nat least 4 seconds, while the other hand isn't keyboard-shaped?\". That works,\nand it's transparent (every alert ships an `explanation` field saying *which*\nsignals contributed and *which* suppressions fired), but it's a ceiling:\n\n- Geometric proximity can't tell *biting* from *resting fingertip on lip*.\n- Per-frame rules don't capture the small temporal signature of the bite\n  itself (the brief inward jaw motion, the fingers pulling away).\n- Tuning is a manual sweep across thresholds, when the dataset already exists\n  to learn them.\n\nWhat I'd love to do — and what I'd love help with — is replace the geometric\nheuristic with a **small learned model**: probably a spatiotemporal CNN or a\nshort-clip video transformer, taking the existing 11-frame clip plus the\nlandmark traces as inputs and outputting a per-event probability for each\nbehaviour. The labelled data is already being collected on every install:\n\n- Multi-frame clips (raw JPEGs + per-frame landmark traces) around every\n  alert and every user-marked missed event.\n- Per-event `verdict` labels (`true_positive` / `false_positive` / `unsure`)\n  added by the user through the same notification or hotkey that produced\n  the alert.\n\nFormat and field-by-field schema are documented in\n[`docs/data-format.md`](docs/data-format.md).\n\n### Where I'd particularly love a second brain\n\n- **Dataset shape**: what makes this dataset usable for training vs. what's\n  missing (hard negatives, inter-rater agreement, privacy-preserving export\n  for cross-user pooling). Some of this is sketched at the bottom of\n  `data-format.md`.\n- **Model architecture**: clip-level vs. landmark-sequence vs. hybrid; how\n  much temporal context actually matters; whether per-behaviour heads share\n  a backbone.\n- **Federated / on-device fine-tuning**: the headline privacy property\n  (nothing leaves your machine) is the whole point of the project, so the\n  ideal training path is one that doesn't violate it — federated, fully\n  on-device, or aggressively-anonymised before any pooling.\n- **Evaluation**: more honest metrics than \"the detector fires on the\n  recorded clips\" — leave-one-user-out, calibration plots, time-to-detect.\n\nIf any of that is your kind of fun, please open a discussion or an issue.\nThis is genuinely a \"would love to work with someone who knows what they're\ndoing\" ask, not a \"PRs welcome\" boilerplate.\n\n## Build from source\n\nRequired: Rust 1.88+, Node.js 22+, pnpm 10+.\n\nSystem packages (Debian / Ubuntu):\n\n```bash\nsudo apt install \\\n    libwebkit2gtk-4.1-dev libgtk-3-dev libglib2.0-dev \\\n    libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev \\\n    libjavascriptcoregtk-4.1-dev libasound2-dev libxdo-dev \\\n    libv4l-dev libssl-dev cmake clang\n```\n\nOn NixOS, `nix-shell` handles all of this for you.\n\n```bash\ngit clone https://github.com/firstdorsal/nailbite.git\ncd nailbite\npnpm install\npnpm tauri dev\n```\n\n### Reproducible release builds (Docker)\n\nThe release pipeline uses these scripts; running them locally produces byte-identical\nartifacts to the published ones.\n\n```bash\nbash build.sh                       # CPU-only AppImage + .deb in ./dist\nGPU_BACKEND=cuda bash build.sh\nGPU_BACKEND=tensorrt bash build.sh\nGPU_BACKEND=rocm bash build.sh\nGPU_BACKEND=migraphx bash build.sh\n\nbash scripts/run-appimage.sh        # build + launch in one step\n```\n\n## Development\n\n```bash\npnpm test                       # Frontend tests (vitest)\ncd src-tauri \u0026\u0026 cargo test      # Backend tests\npnpm lint \u0026\u0026 pnpm typecheck     # Frontend checks\ncd src-tauri \u0026\u0026 cargo clippy    # Backend lint\n```\n\n## Contributing\n\nThis is a personal project, but issues and small PRs are welcome — especially:\n\n- Reports of false positives / negatives with the event clip attached (it's already saved\n  locally, no extra work).\n- New behaviour detectors implementing the `BehaviorDetector` trait.\n- macOS or Windows camera backends behind the existing platform traits.\n\nIf you're trying it out and something feels wrong (it scolds you too much, the sound is too\nloud, the alert dwell is too short), that's exactly the feedback that helps the defaults get\nkinder over time.\n\n## License\n\nGNU Affero General Public License v3.0 — see `LICENSE`.\n\nIn short: you're free to use, modify, and redistribute n**AI**lbite. If you run a\nmodified version as a network service, you have to make your source available\nto its users too. The aim is to keep the tool — and any improvements made to\nit — open for the community that depends on it.\n\n## Acknowledgments\n\n- Hand landmark + palm detection models from [OpenCV Zoo](https://github.com/opencv/opencv_zoo).\n- Face detection + mesh from [IntelliProve](https://github.com/IntelliProve/face-detection-onnx).\n- Pose landmark from [BlazePose via Unity Inference Engine](https://huggingface.co/unity/inference-engine-blaze-pose).\n- Built on the shoulders of [Tauri](https://tauri.app/), [React](https://react.dev/), and\n  [ONNX Runtime](https://onnxruntime.ai/).\n\nAnd a quiet thank-you to anyone whose research on habit reversal and decoupling made this\nworth building in the first place.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirstdorsal%2Fnailbite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffirstdorsal%2Fnailbite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirstdorsal%2Fnailbite/lists"}