{"id":51025390,"url":"https://github.com/edycutjong/scarecrow","last_synced_at":"2026-06-21T19:01:58.958Z","repository":{"id":364246445,"uuid":"1263473828","full_name":"edycutjong/scarecrow","owner":"edycutjong","description":"🔌 Off-grid AI sentry: vision + rules + spoken alerts on a Raspberry Pi ≤4GB","archived":false,"fork":false,"pushed_at":"2026-06-12T08:32:58.000Z","size":5480,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-12T09:24:05.937Z","etag":null,"topics":["computer-vision","dorahacks","edge-ai","offline-ai","qvac","raspberry-pi","sentry","tts"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edycutjong.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-09T01:48:08.000Z","updated_at":"2026-06-12T08:33:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/edycutjong/scarecrow","commit_stats":null,"previous_names":["edycutjong/scarecrow"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/edycutjong/scarecrow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edycutjong%2Fscarecrow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edycutjong%2Fscarecrow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edycutjong%2Fscarecrow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edycutjong%2Fscarecrow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edycutjong","download_url":"https://codeload.github.com/edycutjong/scarecrow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edycutjong%2Fscarecrow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34622271,"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-21T02:00:05.568Z","response_time":54,"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":["computer-vision","dorahacks","edge-ai","offline-ai","qvac","raspberry-pi","sentry","tts"],"created_at":"2026-06-21T19:01:53.154Z","updated_at":"2026-06-21T19:01:58.942Z","avatar_url":"https://github.com/edycutjong.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 🧑‍⚖️ For Judges\n\n**The pitch (30s):** A $50 off-grid Raspberry Pi that *understands* a scene and *reasons* about a plain-English rule — \"alert if a person is near the shed, ignore the dog\" — then speaks the alert and pushes it to a phone. Fully offline, ≤4GB RAM, solar-powered. Not motion detection — multimodal reasoning on the edge.\n\n**Run it in 30 seconds (works on any laptop, no Pi needed):**\n```bash\nnpm install \u0026\u0026 npm start      # → open http://localhost:8080\n```\nThe dashboard shows the live pipeline, camera HUD, rule editor, and event log. Add a rule or \"teach\" a known entity right from the UI.\n\n**Where to look:**\n\n| Judging signal | Where | Status |\n|---|---|---|\n| **Deep QVAC integration** (5 distinct APIs) | `completion` + multimodal, `ragSearch`, `textToSpeech`, `startQVACProvider` — see [Why ONLY QVAC](#-why-only-qvac) | ✅ real `@qvac/sdk` v0.10.2 |\n| **Capability unlock** (RAG known-entity recall) | [`src/core/memory.ts`](src/core/memory.ts) — your car/pet won't trip it, a stranger will | ✅ |\n| **≤4GB on retail hardware** | `/api/status` reports **real measured RSS**; single-model load→infer→unload | ✅ measured, not faked |\n| **True offline** | `python3 scripts/verify_offline.py` — static cloud-import/URL scan + live network probe | ✅ |\n| **Production quality** | `npm test` → **128 unit tests**; `npm run ci` (lint + types + coverage) | ✅ |\n| **Reproducible classification** | `python3 scripts/bench.py` → real precision/recall of the offline baseline over 233 labeled scenes | ✅ |\n\n**Mandatory constraints — all met:** 100% on-device inference through `@qvac/sdk` (**zero cloud APIs** — the hard disqualifier; every interface declared in [`docs/REMOTE_APIS.md`](docs/REMOTE_APIS.md)), [MIT](LICENSE) licensed and fully public, BYOH consumer hardware (Pi 4/5), reproducible via [`scripts/setup-pi.sh`](scripts/setup-pi.sh).\n\n**Evidence bundle:** remote-API declaration ([`docs/REMOTE_APIS.md`](docs/REMOTE_APIS.md) — zero cloud APIs) · structured inference audit log (`GET /api/audit` — model loads/unloads, TTFT, tokens/sec) · offline scan + live network probe (`verify_offline.py`) · real classification baseline over 233 labeled scenes (`bench.py`) · append-only event log (`data/events.jsonl`) · readiness gate (`check_submission_readiness.py`) · physical demo runbook ([`DEMO.md`](DEMO.md)). *Model latency, peak RAM, and mWh/event are captured **on-device** (see [Benchmarks](#-benchmarks)) — not simulated.*\n\n**Radical honesty:** nothing fake is shown as real. The RAM gauge is measured process RSS (tagged `RSS`); battery/solar are *modelled* and clearly tagged **`SIM`** in the UI until an INA219 sensor is wired (`SCARECROW_BATTERY_PCT`/`SCARECROW_SOLAR_W`). Camera capture uses real `libcamera-still` on the Pi. See [Honest Limitations](#-honest-limitations).\n\n---\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/icon-animated.svg\" alt=\"Scarecrow Logo\" width=\"120\"\u003e\n\n  \u003ch1\u003eScarecrow 🔌\u003c/h1\u003e\n  \u003cp\u003e\u003cem\u003e$50 off-grid AI sentry on a Raspberry Pi ≤4GB. Camera → multimodal scene understanding → natural-language rule matching → spoken TTS alerts. Solar/battery powered, fully offline.\u003c/em\u003e\u003c/p\u003e\n  \u003ca href=\"https://youtu.be/fB8f_LXUss4\"\u003e\u003cimg src=\"docs/readme-hero.svg\" alt=\"Scarecrow Hero\" width=\"100%\"\u003e\u003c/a\u003e\n\n  \u003cbr/\u003e\n\n  [![Built for QVAC Hackathon](https://img.shields.io/badge/DoraHacks-QVAC%20Edge%20AI-8b5cf6?style=for-the-badge)](https://dorahacks.io/hackathon/qvac-unleach-edge-ai-i/detail)\n  [![Track](https://img.shields.io/badge/Track-Tinkerer%20(Pi%20≤4GB)-f59e0b?style=for-the-badge)](https://dorahacks.io/hackathon/qvac-unleach-edge-ai-i/tracks#tinkerer)\n\n  \u003cbr/\u003e\n\n  ![Node.js](https://img.shields.io/badge/Node.js-339933?style=flat\u0026logo=node.js\u0026logoColor=white)\n  ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat\u0026logo=typescript\u0026logoColor=white)\n  ![Raspberry Pi](https://img.shields.io/badge/Raspberry_Pi-A22846?style=flat\u0026logo=raspberrypi\u0026logoColor=white)\n  ![QVAC](https://img.shields.io/badge/@qvac/sdk-06b6d4?style=flat)\n  [![CI](https://github.com/edycutjong/scarecrow/actions/workflows/ci.yml/badge.svg)](https://github.com/edycutjong/scarecrow/actions/workflows/ci.yml)\n\n\u003c/div\u003e\n\n---\n\n## 👀 See It Work\n\n📺 **[Watch the full demo on YouTube](https://youtu.be/fB8f_LXUss4)**\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\" valign=\"top\"\u003e\n\n**What the camera sees** *(test clip)*\n\n\u003cimg src=\"docs/screenshots/stimulus-person.jpg\" alt=\"Person approaching the shed at dusk\" width=\"100%\"\u003e\n\nA person in dark clothing approaching the shed at dusk.\n\n\u003c/td\u003e\n\u003ctd width=\"50%\" valign=\"top\"\u003e\n\n**What Scarecrow does** *(real dashboard)*\n\n\u003cimg src=\"docs/screenshots/dashboard-alert.png\" alt=\"Scarecrow dashboard raising a real alert\" width=\"100%\"\u003e\n\n🔴 Reasons about the scene → **THREAT DETECTED** → speaks the alert → logs it.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\" valign=\"top\"\u003e\n\n\u003cimg src=\"docs/screenshots/stimulus-dog.jpg\" alt=\"A dog crossing the yard past the shed\" width=\"100%\"\u003e\n\nA dog trotting across the same yard.\n\n\u003c/td\u003e\n\u003ctd width=\"50%\" valign=\"top\"\u003e\n\n\u003cimg src=\"docs/screenshots/dashboard-dog-clear.png\" alt=\"Scarecrow seeing the dog and staying silent\" width=\"100%\"\u003e\n\n✅ Sees the dog (green box, `0.88`) → matches the *ignore* rule → **PERIMETER CLEAR**, no alert.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\" valign=\"top\"\u003e\n\n**Known-entity recall** *(on-device RAG)*\n\n\u003cimg src=\"docs/screenshots/stimulus-van.jpg\" alt=\"An unfamiliar dark van in the driveway\" width=\"100%\"\u003e\n\nAn **unfamiliar** dark van…\n\n\u003cimg src=\"docs/screenshots/stimulus-truck.jpg\" alt=\"The owner's silver truck\" width=\"100%\"\u003e\n\n…vs. **your** silver truck (taught once).\n\n\u003c/td\u003e\n\u003ctd width=\"50%\" valign=\"top\"\u003e\n\n\u003cimg src=\"docs/screenshots/dashboard-truck-known.png\" alt=\"Scarecrow alerting on the van but recognizing the known truck\" width=\"100%\"\u003e\n\nThe van trips a 🔴 **ALERT**; the silver truck is **recognised** and the alert is **suppressed** — *\"🧩 Recognised known entity: My silver truck.\"* Teach it once; a stranger's vehicle still alerts.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003e **This is reasoning, not motion detection.** Same yard, same camera — Scarecrow\n\u003e alerts on the person and stays silent for the dog because it *understands* the\n\u003e scene against your plain-English rules. The scene clips are stimulus (yours /\n\u003e licensed stock / AI-generated); the dashboard, spoken alert, and RAM gauge are\n\u003e the **real** system on-device. Open it yourself with `npm start`.\n\n---\n\n## 💡 The Problem \u0026 Solution\n\nTraditional security cameras need WiFi, cloud subscriptions, and constant power. In farms, construction sites, and remote properties, none of these are available.\n\n**Scarecrow** turns a $50 Raspberry Pi into an intelligent sentry that understands what it sees:\n\n**Key Features:**\n- 📸 **Vision AI** — QVAC-Vision-1B analyzes camera frames locally\n- 🔒 **NL Rules** — Plain English rules: \"alert if person near shed, ignore dogs\"\n- 🧩 **Known-Entity Recall** — on-device RAG remembers your car/pet/mail carrier and suppresses false alarms\n- 🔊 **Spoken Alerts** — Piper TTS announces threats through a speaker\n- 📲 **P2P Phone Push** — alerts a paired phone over the LAN, no cloud\n- ☀️ **Solar Powered** — PIR-triggered wake with configurable sleep intervals\n- 🖥️ **Pi Dashboard** — live web UI + rule editor served from Pi's hotspot\n\n## 🏗️ Architecture \u0026 Tech Stack\n\n```mermaid\nflowchart TD\n    A[\"⚡ PIR Wake\"] --\u003e B[\"📸 Camera\"]\n    B --\u003e C[\"🧠 QVAC-Vision-1B\"]\n    C --\u003e D[\"📝 Scene Description\"]\n    D --\u003e E[\"🔒 Llama 3.2 1B Rules\"]\n    E --\u003e F{\"Alert?\"}\n    F -- Yes --\u003e K{\"🧩 Known entity? (RAG)\"}\n    F -- No --\u003e H[\"💤 Sleep\"]\n    K -- Known --\u003e H\n    K -- Unknown --\u003e G[\"🔊 Piper TTS + 📲 P2P Push\"]\n    G --\u003e I[\"📊 Event Log + Dashboard\"]\n    I --\u003e H\n    H --\u003e A\n```\n\n| Layer | Technology |\n|---|---|\n| **Runtime** | Node.js on Raspberry Pi 4/5 |\n| **AI Engine** | @qvac/sdk (completion, multimodal, RAG, TTS, P2P) |\n| **Vision** | QVAC-Vision-1B (multimodal) |\n| **Rules** | Llama 3.2 1B (NL evaluation) |\n| **Known entities** | GTE-Large embeddings (on-device RAG) |\n| **TTS** | Piper via @qvac/sdk |\n| **Alerts** | Holepunch-backed P2P phone push |\n| **Dashboard** | Vanilla HTML/JS (Pi hotspot) |\n\n## 💰 Hardware BOM (~$50)\n\n| Part | Cost |\n|---|---|\n| Raspberry Pi 4 (2GB/4GB) | ~$35 |\n| Pi Camera Module v2 | ~$10 |\n| USB Speaker | ~$5 |\n| PIR Motion Sensor (HC-SR501) | ~$2 |\n| Solar panel + battery (optional) | ~$15 |\n\n### 🔌 Wiring\n\n```\n        ☀ Solar ──▶ [ Solar + UPS HAT ] ◀──▶ 🔋 18650 Li-ion\n                           │ 5V / 3A (USB-C)\n                           ▼\n   📷 Camera ──CSI ribbon──▶ ┌────────────────┐ ◀── USB ── 🔊 Speaker\n                            │  Raspberry Pi  │            (spoken alerts)\n   👁 HC-SR501 PIR ──GPIO17──▶│    4 (≤4GB)    │\n                            └────────────────┘\n        (VCC→5V · OUT→GPIO17 · GND→GND — see table)\n```\n\n| Wire | Pi connection | Phys. pin | BCM |\n|---|---|---|---|\n| PIR `VCC` | 5V | 2 | — |\n| PIR `OUT` | GPIO17 | 11 | **17** *(matches `gpio.ts`)* |\n| PIR `GND` | GND | 6 | — |\n| Camera | CSI camera port | ribbon | — |\n| Speaker | USB-A | — | — |\n| Power in | USB-C 5V/3A | — | — |\n\n\u003e The PIR is the **power-aware wake source** — the Pi sleeps until motion, then\n\u003e wakes → captures → infers → sleeps. Change the pin via\n\u003e `startPIRSentry({ pin })`. Solar/battery is optional; any 5V/3A USB-C supply\n\u003e works for a wall-powered build.\n\n## 🏆 Why ONLY QVAC?\n\n| QVAC SDK Method | Scarecrow Usage | Cloud Alternative You'd Need |\n|---|---|---|\n| `loadModel(QVAC_VISION_1B)` | Multimodal scene analysis from camera frames | Google Cloud Vision API |\n| `completion()` + `images` | Describe what the camera sees in natural language | GPT-4 Vision |\n| `completion()` (Llama 1B) | NL rule evaluation: \"alert if person near shed\" | OpenAI API |\n| `ragIngest()` / `ragSearch()` | Recognize known-friendly entities, suppress false alarms | Pinecone + embeddings API |\n| `textToSpeech()` (Piper) | Spoken alerts when rules match | Amazon Polly |\n| `startQVACProvider()` (P2P) | Push alerts to a paired phone over the LAN | Firebase Cloud Messaging |\n| `unloadModel()` | Critical — free RAM between model swaps on 4GB Pi | N/A |\n\n**Take QVAC out and you'd need 5+ cloud services** (Google Vision + OpenAI + Pinecone + Amazon Polly + Firebase), a WiFi router, and a power outlet — destroying the entire $50 off-grid premise.\n\n## 🚀 Getting Started\n\n```bash\ngit clone https://github.com/edycutjong/scarecrow.git\ncd scarecrow\nnpm install\npython3 scripts/seed.py\n\n# Start the sentry loop AND serve the live dashboard over the Pi hotspot\nnpm start                 # → http://192.168.4.1:8080 (PORT env to override)\n\n# Sentry loop only, no dashboard (headless)\nnpm run sentry\n```\n\nThe dashboard polls a tiny zero-dependency HTTP API (`node:http`) for live data:\n\n| Endpoint | Returns |\n|---|---|\n| `GET /` | Live dashboard (camera HUD, pipeline, rules, events) |\n| `GET /api/status` | Stage, RAM, battery, solar, frames, armed state |\n| `GET /api/events` | Event log from the sentry loop |\n| `GET /api/rules` · `POST` · `DELETE /api/rules/:id` | List / add / remove natural-language rules |\n| `GET /api/entities` · `POST` · `DELETE /api/entities/:id` | List / teach / forget known-friendly entities |\n| `GET /api/audit` | Structured inference audit log + summary (model loads/unloads, TTFT, tokens/sec) |\n| `GET /api/health` | Liveness probe |\n\n\u003e Opened directly as a file (`open src/web/index.html`) the dashboard runs in a\n\u003e self-contained **simulation**; pointed at the API it switches to **LIVE** mode\n\u003e automatically — and the rule/entity editors become live. On an alert, a P2P\n\u003e phone push is sent via `@qvac/sdk` (`src/core/p2p.ts`).\n\n### 🧩 Known-entity allow-list (on-device RAG)\n\nTeach Scarecrow what to ignore — *\"my silver truck,\" \"the mail carrier,\" \"my dog\nMax.\"* Before raising an alert, `src/core/memory.ts` runs an on-device semantic\nsearch (`ragIngest`/`ragSearch`, GTE-Large embeddings) against the scene; a match\nabove threshold **suppresses the alert**. This is reasoning, not a blocklist: the\nowner's car pulling in won't trip it, but a stranger's will.\n\n### 🔌 Power modes\n\n| Command | Behaviour |\n|---|---|\n| `npm start` | Dashboard + fixed-interval sentry loop |\n| `npm run sentry` | Headless fixed-interval loop |\n| PIR mode (`startPIRSentry`, `src/core/gpio.ts`) | Sleep → **motion-triggered wake** → check → sleep (solar-optimal) |\n\nEvents are persisted append-only to `data/events.jsonl` (`src/core/storage.ts`) and\nrestored on boot — a zero-dependency, inspectable offline evidence trail.\n\n**Honest telemetry.** `/api/status` reports **real measured process RSS** for the\nRAM gauge (`process.memoryUsage().rss`, tagged `RSS` in the UI). Battery/solar are\n**modelled** and tagged **`SIM`** on the dashboard until a real sensor is wired —\nexport `SCARECROW_BATTERY_PCT` / `SCARECROW_SOLAR_W` (e.g. from an INA219 daemon)\nand the values go live with `powerSimulated: false`.\n\n### 🍓 Provisioning a Pi\n\n```bash\nbash scripts/setup-pi.sh        # camera, audio, Node, deps\nbash scripts/setup-pi.sh --ap   # also configure the Scarecrow-AP hotspot\n```\n\n## 📊 Benchmarks\n\n**Classification quality (real, reproducible anywhere — `python3 scripts/bench.py`).**\nPrecision/recall of the documented *offline keyword-fallback baseline* over the 233\nlabeled fixture scenes — the floor the Llama 3.2 1B engine improves on:\n\n| Baseline metric | Value |\n|---|---|\n| Precision | 0.67 |\n| Recall | 0.43 |\n| F1 | 0.52 |\n| Accuracy | 0.73 (169/233) |\n\n\u003e The modest recall is the point: a keyword baseline misses non-\"person\" alerts\n\u003e (vehicles, etc.), which is exactly why on-device LLM reasoning is used. The\n\u003e Llama model's real precision/recall is measured **on-device** (needs the model).\n\n**Latency · RAM · power — measured on the Pi, not simulated.** These depend on the\nQVAC models and physical rig, so they are captured on-device (the dashboard\n`/api/status` reports **live RSS**; `htop`/`vcgencmd` for memory; INA219 for\nmWh/event). Targets for Raspberry Pi 4 (4GB):\n\n| Stage | Target |\n|---|---|\n| Vision analysis (1 frame) | \u003c 4,000 ms |\n| Rule evaluation | \u003c 2,000 ms |\n| TTS alert | \u003c 2,000 ms |\n| Full pipeline | \u003c 8,000 ms |\n| Peak RAM | \u003c 3,800 MB (≤4GB constraint) |\n\n\u003e These are budgets, not measurements — fill them from a real Pi run for the\n\u003e submission. The benchmark script never fabricates device numbers.\n\n## 🧪 Testing \u0026 CI\n\n**128 unit tests** (vision, rules + live editing + real fallback classification over the 233-scene fixture set, power state machine, telemetry, inference audit log, RAG known-entity recognition, P2P phone alerts, PIR wake, JSONL persistence, dashboard API) + offline verification scan + readiness suite. Run `npm test`. *(Tests mock only the `@qvac/sdk`/hardware boundary; every assertion exercises real project logic — no tautological label-fed tests.)*\n\n**4-stage pipeline:** Quality → Security → Offline Verify → Deploy\n\n```bash\npython3 scripts/verify_offline.py\npython3 scripts/bench.py\npython3 scripts/check_submission_readiness.py\n```\n\n| Layer | Tool | Status |\n|---|---|---|\n| Code Quality | ESLint + TypeScript | ✅ |\n| Security (SAST) | CodeQL | ✅ |\n| Dependency Audit (SCA) | npm audit + Dependabot | ✅ |\n| Secret Scanning | TruffleHog | ✅ |\n| Offline Verification | verify_offline.py (cloud-import/URL scan + net probe) | ✅ |\n\n## 📁 Project Structure\n```\nscarecrow/\n├── docs/               # README assets\n├── data/fixtures/      # test_rules.json, test_scenes.json\n├── scripts/            # setup-pi, seed, bench, verify, readiness\n├── src/\n│   ├── core/\n│   │   ├── qvac.ts     # @qvac/sdk wrapper\n│   │   ├── vision.ts   # Camera + multimodal analysis\n│   │   ├── rules.ts    # NL rule engine + add/remove\n│   │   ├── memory.ts   # RAG known-entity allow-list (ragSearch)\n│   │   ├── power.ts    # Sentry loop + live telemetry (getSystemStatus)\n│   │   ├── gpio.ts     # PIR motion-wake source\n│   │   ├── storage.ts  # Append-only JSONL event persistence\n│   │   ├── p2p.ts      # P2P phone push alerts on match\n│   │   └── audit.ts    # Structured inference audit log (loads/unloads, TTFT, tok/s)\n│   └── web/\n│       ├── server.ts   # Dashboard HTTP server + JSON API (node:http)\n│       └── index.html  # Live dashboard + rule/entity editor (Pi hotspot)\n├── .github/            # CI/CD + CodeQL + Dependabot\n└── README.md\n```\n\n## ⚠️ Honest Limitations\n\n1. Camera capture simulated in dev (real Pi Camera required)\n2. PIR wake uses the `onoff` GPIO binding on the Pi; dev/CI run a simulated motion source\n3. Battery/solar are modelled (tagged `SIM` in the UI) until an INA219 sensor feeds `SCARECROW_BATTERY_PCT`/`SCARECROW_SOLAR_W`; the RAM gauge is real measured RSS\n4. Event log persists to append-only JSONL; a queryable SQLite store is future work\n5. Single-model-at-a-time due to 4GB constraint\n6. English rules only\n\n## 📄 License\n[MIT](LICENSE) © 2026 Edy Cu\n\n## 🙏 Acknowledgments\nBuilt for **QVAC Hackathon I — Unleash Edge AI** (DoraHacks). Proving that useful AI doesn't need the cloud — just a $50 Pi and QVAC.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedycutjong%2Fscarecrow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedycutjong%2Fscarecrow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedycutjong%2Fscarecrow/lists"}