knotwork
Health Warn
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Fail
- rm -rf — Recursive force deletion command in .github/scripts/run-instrumented-selftest.sh
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
An AI agent you build, not just prompt. Local-first Android agent with on-device inference (LiteRT-LM), pipelines you assemble yourself, MCP tool calling, and your confirmation before any risky action.
Knotwork — on-device AI agent for Android
An AI agent you build, not just prompt. A local-first Android agent whose
behaviour you assemble from explicit, verifiable blocks — and every risky
action waits for your confirmation. It plans and acts across your phone, and a
typical conversation never leaves the device.
What it is
Knotwork is an autonomous assistant that takes a request in plain language,
decides what to do, and carries the work out across your Android device.
Inference runs on-device via LiteRT-LM
(Google Edge AI, the successor to TensorFlow Lite), so planning, tool calls,
and replies stay on the phone unless you deliberately reach for a cloud model.
What makes it Knotwork rather than another chat box is that you build the
behaviour. Every conversation is processed by a pipeline — what Tasker, n8n,
or Zapier would call a workflow: a graph of typed nodes (input, on-device LLM,
optional cloud LLM, tool calls, routing, decomposition, evaluation,
clarifications, output) that you can edit inside the app or in a standalone
browser editor. Automations that can act on their own still can't act
unsupervised: destructive or sensitive tool calls pass through a
human-in-the-loop gate, so the agent never sends a message or writes a file
without showing you the request first.
Who it's for
Knotwork is built for the technically literate flagship-Android owner who
values privacy as a practice rather than a slogan, enjoys constructing the way
their tools behave, and is already at home in the world of Tasker, Obsidian,
Home Assistant, or r/LocalLLaMA. If you want to know — and control — exactly
what your agent does with your data, this is for you.
It is deliberately not a mass-market "ask a question, get an answer"
assistant. For that, Gemini is free and built into the OS, and competing there
is a losing bet regardless of code quality. Knotwork trades that convenience for
control, transparency, and local-first data handling.
See it work
The Share Handler scenario is a good one-glance picture of the whole idea:
share an article, a message, or a screenshot to Knotwork from any app, and an
on-device model turns it into a clean, structured note in your inbox — pausing
to ask before it writes the file. On-device inference, a real tool call, and the
human-in-the-loop gate, all in one flow.
First launch leads with what the agent should do for you rather than an empty
canvas: pick a ready-made scenario and Knotwork materialises its pipeline as
your default, wires any surface it uses, and downloads exactly the model that
scenario needs — with size and live progress — before dropping you into a
working chat. A Start from scratch path stays one tap away.
https://github.com/user-attachments/assets/2ea06de5-6832-4e0c-ad48-430f375d8b72
The hero above is a real on-device capture — no mockups.
Highlights
- Runs on your device. On-device LLM inference through LiteRT-LM, with
optional NPU/GPU acceleration; CPU-only works too, just slower. Cloud
providers (OpenAI, Anthropic, Google Gemini, DeepSeek, Ollama) are optional,
opt-in, and bring-your-own-key — nothing leaves the device unless you
configure it to. - You build the pipeline. A drag-and-drop editor with pan / pinch-zoom,
snap-to-grid, a radial node picker, auto-layout, inline validation, run-trace
overlay, and per-type configuration for all 14 node types — plus a standalone
browser editor for authoring pipelines without
launching the app. Prompt variables ($DATE,$TIME,$TOOLS,$MODEL,$MEMORY_SUMMARY,$LANG,$LOCATION,$USER,$DEVICE) render fresh on
every run. Prompts import and export as Markdown files, so one can be
written in any editor and passed around — and a prompt file supplies wording
only: it cannot add tools or steps, and a file that asks is imported as text
with the request named. - Tools, gated by you. Local actions through AppFunctions Jetpack and
external servers through the Model Context Protocol (MCP), with reusable
skills (instruction + tool allowlist + context) as pipeline steps. Every
sensitive or destructive call stops for explicit confirmation — the allowlist
is enforced at the executor level, not merely suggested. - Reaches you from outside the app. A share target, launcher shortcuts, and
a Quick Settings tile run your chosen pipeline over shared text/images or in
the background. Triggers (time, charging, Wi-Fi, network) fire a pipeline
on their own and report back with a notification. Every entry surface stays
inert until you bind a pipeline to it — a privacy-first default. - Complements your automation app. Tasker, MacroDroid or a shell script over
adbcan ask the agent to run one pipeline you nominate: they decide when,
the agent does the language-model part of what. Off by default and behind an
explicit consent dialog; the binding is an allowlist, not a fallback, so a
request naming anything else is refused rather than redirected. Every inbound
request — accepted or refused — lands in a readable journal. - Local-first by construction. The Room database is SQLCipher-encrypted and
API keys are sealed with AES-GCM under a dedicated Android Keystore key.
On-device usage statistics (a build-time guard forbids any network on that
path), attachment images that never reach cloud models, and voice input
transcribed on-device before the pipeline runs. A FOSS build ships with
zero proprietary dependencies for F-Droid. - Reliable over long, autonomous runs. A validate-and-repair gate keeps
structured nodes producing well-formed output, exponential-backoff retry
smooths transient cloud failures, and background history compression keeps a
long session from overflowing the context window. Interrupted runs resume from
their last completed node; reopening a chat reconnects to a run still
executing in the background. A run nobody is watching is bounded by step and
token ceilings counted across the whole run tree — so an automation that
starts looping cannot quietly spend your cloud key — and a run stopped that
way says so instead of looking broken. - Remembers what matters. Long-term memory with semantic retrieval (RAG)
over past conversations, automatic fact extraction, manual "Save to memory,"
and a memory manager with search, provenance, compaction, and JSON
export / import. - A chat list that stays a working list. Archive a conversation to take it
out of the drawer without deleting a single message: it waits, whole and
exportable, on its own screen until you restore it — and opens read-only in
the meantime, so nothing brings it back except you.
The full feature tour lives in the user guide.
Screenshots
The pipeline editor below is a capture from a phone running the app. The other
three are rendered at 1080 × 2400 from a Roborazzi baseline
(./gradlew :catalog:recordRoborazziDebug --tests "*HeroSnapshotTest*"), which
keeps them and the design-system regression suite in sync — the editor canvas is
an app screen rather than a design-system component, so it has no baseline to
render from. Hover over (or tap) an image to see the dark variant via your
browser's prefers-color-scheme.
Pipeline editor — a 22-node pipeline on the canvas: routers, nested sub-pipelines, queues, tools, on-device and cloud steps |
Pipeline library — saved pipelines + import-from-browser-editor JSON |
Tools — built-in AppFunctions + MCP servers with per-tool risk + toggle |
Settings — searchable hub with inline Basic controls over eight category sub-screens |
Requirements
- Android 14 or newer (API level 34+). A few OS-integration features need
Android 16: calling functions that other apps expose, and the strict
intent-matching rules that harden the external-automation entry point. On
Android 14 and 15 everything else — local models, cloud providers, MCP
servers, triggers, scheduled tasks — works unchanged. - Approximately 2 GB of free RAM available for the LLM at runtime.
- Optional: hardware acceleration via NPU or GPU for noticeably faster
inference. CPU-only operation works but is slower. - Optional: location permission, requested only if you scope a Wi-Fi
trigger to specific network names (Android ties the Wi-Fi name to location).
Nothing else uses it, and the name never leaves the device. - To build from source: JDK 21 and the Android SDK (the Android Studio
bundled JBR already ships JDK 21). See CONTRIBUTING.md for
the full dev setup.
Install
From Google Play
Knotwork on Google Play
— the simplest route, and the one that updates itself. Requires Android 14+.
The Play build is the full flavour described below. It shares a signing key
with the APKs on the Releases page, so you can move between those two channels
without reinstalling.
From a release build
Grab the latest APK from the
Releases page and install it
on an Android 14+ device. Two flavours are published:
full— the standard build, with opt-in Firebase Crashlytics for
anonymous crash reporting (off by default, never collects message content).foss— zero proprietary dependencies and no crash reporting, suitable
for F-Droid. See docs/release.md § FOSS / F-Droid build.
Upgrading from
0.6.0or earlier requires a clean install. Those builds
were signed with the Android debug keystore;0.7.0is the first release
signed with a real release key. Android refuses to update an install whose
signer changed, so you must uninstall the old build — which clears its local
data — before installing0.7.0. Export anything you want to keep first.
This is a one-time break; later releases update in place. See the full
pre-release notice below.
Build from source
git clone https://github.com/alexeyw/knotwork.git
cd knotwork
./gradlew assembleFullDebug
adb install app/build/outputs/apk/full/debug/app-full-debug.apk
First run
Launch the app and let scenario onboarding guide you: pick a scenario and
Knotwork sets up its pipeline and downloads exactly the model it needs, then
opens into a working chat once the model has warmed. Prefer to wire things up
yourself? Choose Start from scratch, then open Models, download a
LiteRT model through the built-in manager (or paste a custom URL), and send your
first message once it loads.
Tech stack
| Layer | Technology |
|---|---|
| Language | Kotlin |
| UI | Jetpack Compose + Material Design 3 |
| Design system | Knotwork (in-tree :catalog library) |
| Brand fonts | Inter + JetBrains Mono (bundled TTF, SIL OFL 1.1) |
| LLM engine | LiteRT-LM (Google Edge AI / ex-TensorFlow Lite) |
| Tool calling | AppFunctions Jetpack |
| MCP & cloud LLM | Koog (MCP transport + cloud-LLM client only) |
| Architecture | Clean Architecture + MVVM |
| DI | Hilt |
| Async | Coroutines / Flow |
| Serialization | kotlinx.serialization (structured-output validation) |
| Network | OkHttp + Ktor (via Koog) |
| Image loading | Coil 3 (attachment thumbnails / viewer) |
| Local storage | Room + DataStore |
| Crash reporting | Firebase Crashlytics (full flavour) / none (foss) |
| Distribution | full (Play / direct APK) + foss (F-Droid, no Google) |
| Testing | JUnit + MockK; instrumented suite on an emulator matrix in CI |
| Architecture tests | Konsist (Clean-Architecture layer guard, in check) |
Privacy
Knotwork has no account, no sign-in, and no server of its own. There is nothing
to log into, and nothing is uploaded for the app to work.
- Conversations stay on the device by default. Inference runs locally
through LiteRT-LM. Data leaves the phone only through a path you configured
yourself: a cloud LLM node with your own API key, or an MCP server you added.
Both are opt-in and both are visible in the pipeline you built. - Usage statistics are local-only. The in-app statistics are computed and
stored on the device and are never transmitted; a build-time architecture
guard fails the build if any network dependency reaches that code. Exporting
them is a manual action you take. - Crash reporting is opt-in and off by default. In the
fullflavour you
can enable anonymous crash reports (stack traces, device model, Android and
app version, the active pipeline and model identifiers). Message content,
prompts, memory, and API keys are never included. Thefossflavour has no
crash-reporting dependency at all and hides the setting. - Sensitive data is encrypted at rest. The local database — chats, long-term
memory, run traces — is SQLCipher-encrypted, and API keys, the Hugging Face
token, and MCP credentials are sealed with AES-GCM under a dedicated Android
Keystore key. - The agent asks before it acts. Destructive and sensitive tool calls stop
at a human-in-the-loop confirmation, including when a pipeline runs in the
background from a trigger.
The policy in full — what is stored on the device, every path that can send
data off it, and how to switch each one off — is PRIVACY.md. The
threat model behind it, including what is explicitly out of scope, is in
SECURITY.md; the per-feature behaviour is in the
user guide.
Documentation
- Architecture overview — docs/architecture.md.
- User guide — docs/user-guide.md.
- External-automation contract (calling the agent from Tasker, MacroDroid or
adb) — docs/external-automation.md. - Extending the agent (new node types, tools, providers, prompt
variables) — docs/extending.md. - Code style — docs/code-style.md.
- Testing strategy and coverage — docs/testing.md.
- API & integration conventions — docs/api-conventions.md.
- Release-build playbook (R8, signing, AAB, APK size) — docs/release.md.
- Decisions that constrain contributions — docs/decisions/.
- Roadmap — docs/roadmap.md.
- Contributing guide — CONTRIBUTING.md.
- Code of Conduct — CODE_OF_CONDUCT.md.
- Privacy policy — PRIVACY.md.
- Security policy and threat model — SECURITY.md.
- Release notes and version history — CHANGELOG.md.
Pre-release notice
This project is currently at version 0.8.0 and is published for review and
experimentation. Expect rough edges:
- There are no stability guarantees for the public surface (Kotlin APIs,
pipeline JSON schema, settings layout) between versions. - On-device storage formats (encrypted preferences, exported pipeline JSON)
may still change between versions. - Shared pipeline files are not yet a compatibility contract. Exported
pipelines and bundles carry a version stamp (schemaVersion: 1,bundleVersion: 1), but before 1.0 that stamp is a marker, not a promise:
the schema may change without a major bump, and no import-time migration is
provided. A file whose stamp does not match the build importing it is not
rejected — it is imported on a best-effort basis behind an explicit
warning. What the import does now tell you is exactly which settings it
could not read, by name, whether or not the stamp matches — because the
format adds fields without bumping the stamp, so a matching version is no
guarantee that nothing was lost. Keep the original file anyway: naming the
loss is not the same as preventing it. At 1.0 at
the latest the format becomes a semantic-versioning contract: a breaking
change then means a majorschemaVersionplus a migration applied on import. - Upgrades preserve local data. Every Room schema-version bump ships with
an explicit migration, so an in-place update keeps your chat history,
long-term memory, run traces, custom pipelines, and saved presets / prompt
templates. (Note: downgrading to an older build recreates the database
empty — forward migrations cannot be reversed — so export anything you want
to keep before installing an older version.) - The signing identity changed at
0.7.0— a one-time upgrade break.
Builds up to and including0.6.0were signed with the Android debug
keystore;0.7.0is the first release signed with a real release key.
Android refuses to update an install in place when the signer changes
(signature mismatch), so upgrading from0.6.0or earlier means uninstalling
the old build first — which clears its local data. Export anything you want
to keep before you do. Releases from0.7.0onward share one signer and
update in place normally.
License
Released under the Apache License 2.0. See LICENSE for the full
text.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found