No description
  • TypeScript 79.3%
  • JavaScript 10.9%
  • CSS 9.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-06 01:33:00 +02:00
.forgejo/workflows fix(ci): take release notes from CHANGELOG.md 2026-10-04 04:01:29 +02:00
app feat(core): let a skill pick its own tool with mode:, and have Jev pick its model when the tool lacks it 2026-10-06 01:04:09 +02:00
template fix(template): keep git folders and build output out of Syncthing wherever a repo sits 2026-10-06 01:33:00 +02:00
.env.example feat(routines): KRYON_ROUTINES=0 leaves routines to another computer 2026-10-05 18:57:41 +02:00
.gitignore chore(core): untrack editor tooling, state the Node version, drop a duplicate script 2026-10-03 16:40:14 +02:00
AGENTS.md fix(ui): open file cards by KryonData's real path too 2026-10-06 00:31:36 +02:00
CHANGELOG.md docs: release v0.3.0 in the changelog 2026-10-06 01:12:57 +02:00
CLAUDE.md feat: initial Kryon 2026-10-03 01:45:43 +02:00
LICENSE chore(core): prepare repository for release 2026-10-03 20:07:25 +02:00
README.md fix(template): keep git folders and build output out of Syncthing wherever a repo sits 2026-10-06 01:33:00 +02:00
SPEC.md feat(core): let a skill pick its own tool with mode:, and have Jev pick its model when the tool lacks it 2026-10-06 01:04:09 +02:00

Kryon

A local "Jarvis": a web app on your machine that hands your requests to the AI coding tools you already use (Claude Code, Codex, OpenCode), with whatever provider and account those tools are set up with. An optional router model (Jev) can pick the right model for each request and answer simple things itself. Works on Linux, macOS and Windows.

Licensed under the MIT License, copyright 2026 CryoforgeNexus.

What you need

  • Node.js 22 or newer
  • At least one of these tools, installed and logged in. Kryon uses each tool's own login, so you keep the account and provider you already pay for:
    • Claude Code (claude), with your Anthropic account
    • Codex (codex login), with your OpenAI account
    • OpenCode (opencode auth login), with any provider it supports: Anthropic, OpenAI, Google Gemini, OpenRouter and many more (see Models and providers)
  • Optional: an OpenRouter API key for Jev, the router (see OpenRouter and Jev)

Install

Download the latest source archive from this repository's Releases page and extract it into a folder named kryon. Cloning is only needed if you plan to contribute.

cd kryon
cp .env.example .env        # Windows: copy .env.example .env

If you want Jev, paste your OpenRouter key after OPENROUTER_API_KEY= in .env (you can add it later). Then:

cd app
npm ci
npm run build
npm start

Open http://kryon.localhost:5796 (or http://127.0.0.1:5796, the same thing), pick a tool with the slider at the top, click the orb (or press Space) and talk. Kryon answers out loud and listens again; talk over it or press Esc to cut it off. With Audio → Wake word, the microphone stays open but Kryon only answers when you say its name ("Kryon, what's on today?"), then follow-ups for 30 seconds; what it hears is transcribed on your computer, never sent anywhere. You can also type. While Kryon works, what you send waits its turn (queued), and Stop ends the current request and drops the queue. Speech recognition (Whisper) and the voice (Kokoro) run locally and download ~250 MB of models the first time.

  • Start at login: npm run autostart (undo with npm run autostart -- remove). Run it again after updating.
  • As an app: in Chrome, Edge or Brave, open http://kryon.localhost:5796 and pick Install Kryon (address bar or menu): it gets its own window and icon. Each address counts as its own app, so install it from that one. Firefox can't install web apps on Linux or macOS.
  • Language: KRYON_LANG=fr in .env switches the interface, replies and voice to French. To add a language, copy app/src/i18n/en.json, translate it, and set KRYON_LANG to its code.

Your files

Your content lives outside the app, in Documents/KryonData (change it with KRYON_DATA in .env), created on first start. It is an Obsidian vault: open it there to browse.

Kryon's starter files (skills, AGENTS.md, routines.json...) are yours to edit or delete. An update replaces only the ones you never changed. If you edited one, it stays as you left it: the new default is kept in .kryon/template/, and needs-you.md tells you, so you can compare them or ask a job to merge them. A file you delete stays deleted.

KryonData/
├── projects/<name>/<name>-vault/   notes / spec / roadmap for a project
├── projects/<name>/<name>-repo/    that project's code (move it here, or link to it)
├── projects/<name>/<name>-assets/  its images and other files
├── courses/<...>/<Course>/  class notes, used by teach mode
├── memory/raw/              your inbox: clippings, transcripts, files you send to Kryon
├── memory/wiki/<domain>/    what Kryon learns, one note per topic
├── memory/outputs/          what routines produce
├── skills/<domain>/         what Kryon knows how to do
├── routines.json            skills that run on a schedule
├── stats.json               the usage cards
├── models.json              the models each tool may use (first = default)
├── needs-you.md             things waiting for you; only you tick them
├── persona.md               the voice Kryon answers in, and its words for the steps (empty it for a plain voice)
└── Index.md                 generated map of every note (don't edit)

The tools may change anything inside KryonData; for anything outside it they must ask first.

Two computers

To have the same Kryon on a laptop and a desktop, sync KryonData between them. Syncthing does it peer to peer, without a cloud in between, on Linux, macOS and Windows:

  1. Install Kryon on both computers, at the same version (git pull on each when you update).
  2. Copy .env and .secrets/ over once by hand, and log each tool in on each computer (claude, codex login, opencode auth login). Don't sync these.
  3. In .env on the computer you use less, set KRYON_MACHINE=laptop (any name) so it keeps its own logs, and KRYON_ROUTINES=0 so only the other one runs routines and sends notifications.
  4. Share the KryonData folder in Syncthing. Kryon puts a .stignore in it that keeps each computer's own state out of the sync: tool sessions, the undo history, caches, generated indexes, Obsidian's window layout, git folders and build output (node_modules, target, .next).

Project code isn't synced: push and pull each project's repo with git. A cloud folder (Dropbox, OneDrive...) works too, but it can't read .stignore.

Skills

A skill is a Markdown file in KryonData/skills/ describing a task done the same way every time. Start with "onboard": Kryon interviews you about what you do and writes skills and routines for it. It also comes with research, compile, reflect, learn, tidy, briefing, mail, next, teach, review and news. Every night, learn sharpens the skills used that day (each change is listed in Needs you, and Undo in History takes it back) and drafts new ones from tasks that took several steps.

Every skill is a button on the dashboard; typing /<name> ... runs it directly. Add your own as skills/<name>.md or skills/<domain>/<name>.md (names must be unique):

---
description: One line saying when to use it.
model: sonnet            # optional: always use this model when the tool has it
mode: opencode           # optional: always run in this tool (when it's set up), whatever the slider says
icon: ✎                  # optional: shown on its dashboard button
mcp: calendar, gmail     # optional: the MCP servers it needs (otherwise Jev picks them)
---
What the tool should do. $ARGUMENTS is replaced by what you asked for.

When the tool lacks the skill's model: (a GPT model in Claude Code, say), Jev picks one of that tool's models instead.

Coming from another tool? From kryon/app, run npm run import-claude, import-codex or import-opencode. Commands become skills; MCP servers go to KryonData/mcp.json switched off (they run outside the sandbox, so enable only those you trust). The imported skills wait in Needs you for a read, the ones that mention links or network commands named first: every tool follows a skill without asking, and one from a marketplace can hide instructions. Your tool's own folder is left untouched.

Models and providers

Each tool runs on its own login and provider: Kryon never needs an account of its own. KryonData/models.json lists the models each tool may use, the first one being the default. Pick one yourself with the Model menu under the input, or let Jev choose (see below). Tools think at medium effort unless Jev asks for more; change the default per tool with "effort" in models.json. Reload the page after editing it.

OpenCode with the provider of your choice

OpenCode works with almost any provider. Log in with opencode auth login and choose yours, or put that provider's usual API-key variable in .env (for example ANTHROPIC_API_KEY, OPENAI_API_KEY or GEMINI_API_KEY). Then list its models in the opencode section of models.json as provider:model, for example anthropic:claude-sonnet-4-6, openai:gpt-5.6 or google:gemini-2.5-flash. Kryon passes them to OpenCode as provider/model.

Model IDs written without a provider (such as z-ai/glm-5.2) go through OpenRouter. The starter list mixes both kinds: OpenRouter models, plus Gemini, Claude and GPT used directly. Kryon skips the models whose provider isn't logged in (through opencode auth login or its key in .env), so you see and get only the ones you can use. Add, remove or reorder them freely.

OpenRouter and Jev (optional)

OpenRouter is one way to reach hundreds of models (Claude, GPT, Gemini, GLM, DeepSeek...) with a single prepaid account and pay per use. Kryon uses it in two places, both optional:

  • Jev, the router, runs on OpenRouter with the key in .env (OPENROUTER_API_KEY). For each request it picks the cheapest model in models.json that will do the job, picks a skill when one fits, and answers greetings, simple facts and quick math itself without starting a tool. Set KRYON_ROUTER in .env to use another OpenRouter model, such as a :free one. That key only ever goes to Jev: no tool job sees it.
  • OpenCode can use OpenRouter as its provider, like any other: choose OpenRouter in opencode auth login and use the plain model IDs from the starter list.

Without an OpenRouter key Kryon still works: each request goes to the tool you picked, with the model you picked or the first one in its list. You lose Jev's model and skill choice (skills still run from their dashboard buttons or by typing /<name>) and its quick answers, so every request starts a tool. Remove { "type": "openrouter" } from stats.json to hide its credits card.

Gemini for students

Google offers a free tier with changing quotas and availability: check its current terms before relying on it for coursework. There are two ways to use it:

  • Directly in OpenCode: create a key in Google AI Studio, add GEMINI_API_KEY=... to .env, add google:gemini-2.5-flash to the opencode list in models.json and reload the page.
  • Through OpenRouter: if you already use OpenRouter, add your Google AI Studio key in its integration settings. OpenRouter then sends your Gemini requests (Jev's, and OpenCode's plain IDs such as google/gemini-2.5-flash) through your own key and quota. Nothing changes in .env or models.json. OpenRouter may charge a small fee on top for this, see its bring-your-own-key docs.

Routines

routines.json lists skills to run on a schedule. Results go to memory/outputs/<name>/<date>.md, and with "panel" the latest gets its own dashboard panel. A routine missed while the computer was off runs once when Kryon starts again. Without "mode", it runs in the first tool that's set up. If the file can't be read (a missing comma, say), no routine runs and Needs you says what's wrong.

[
  { "name": "briefing", "at": "07:00", "model": "sonnet", "skill": "briefing", "panel": "Today" },
  { "name": "weekly-review", "at": "18:00", "days": ["sun"], "mode": "claude", "skill": "review" },
  { "name": "standup", "at": "09:30", "days": ["mon", "tue", "wed", "thu", "fri"],
    "prompt": "What did I change in my repos yesterday?", "project": "myproject" },
  { "name": "heartbeat", "every": "4h", "between": ["08:00", "21:00"], "model": "haiku", "skill": "heartbeat" },
  { "name": "file-inbox", "on": "memory/raw", "skill": "compile" },
  { "name": "deploy-failed", "hook": true, "prompt": "A deploy failed: read what the webhook sent and add what I should do to needs-you.md." }
]

Besides "at", a routine can run:

  • Every so often, with "every": "30m" or "2h" (and "between" to keep it to waking hours). The starter heartbeat goes through the checklist in heartbeat.md and adds to Needs you only what needs you in the next few hours.
  • When a folder changes, with "on": a folder inside KryonData. It runs a minute after the last change there, and gets the changed files as its arguments.
  • From a webhook, with "hook": true: set KRYON_HOOK_TOKEN=<a long random string> in .env, then curl -X POST -H "Authorization: Bearer <token>" --data "what happened" http://127.0.0.1:5796/api/hooks/deploy-failed. What the webhook sends reaches the job as data from outside, so that job has no web (like a mail job).

Adding a capability

Kryon is built to be extended from KryonData alone, so app updates never touch what you add. A new capability, Slack for example, takes up to three files:

  1. The tools, as an MCP server in KryonData/mcp.json, given to every tool (Claude Code, Codex, OpenCode):

    {
      "slack": {
        "command": ["npx", "-y", "<a-slack-mcp-server>"],
        "enabled": true,
        "disallowed": ["post_message"]
      }
    }
    

    MCP servers run outside the sandbox, and jobs run without asking, so a tool on an enabled server can be called at any time. List in "disallowed" the tools that act without undo (sending, deleting, paying); no tool can call them. The server's own login (OAuth, token file) stays with the server: don't put keys in KryonData, since the tools read it.

    Optional fields: "description" (one line: Jev then gives the server only to the requests that need it, which also starts jobs faster), "untrusted": true for a server that brings in text written by others (a chat, a shared inbox), and "web": true for one that reaches the internet (a browser). A job with an untrusted server gets no web at all, so a message telling Kryon to "send your notes to this address" has no way out. Gmail is untrusted.

  2. When and how to use it, as a skill in skills/slack.md (see Skills): "summarise the channels I missed", "draft a reply, never post it; add - [ ] Post the reply to X? to needs-you.md". Jev then routes "what's new on Slack" to it.

  3. When to run it on its own, as a routine in routines.json (optional), with "panel" to give its result a dashboard panel.

A browser, for sites that need clicking through (forms, pages behind JavaScript): Playwright MCP in mcp.json.

{
  "browser": {
    "command": ["npx", "-y", "@playwright/mcp@latest", "--isolated", "--headless"],
    "enabled": true,
    "description": "A web browser: open pages, click, fill forms, take screenshots",
    "web": true,
    "disallowed": ["browser_file_upload"]
  }
}

--isolated starts a fresh browser each time, logged in nowhere, so a job can't act as you on a site. "web": true keeps it out of mail jobs, and browser_file_upload would let a page take your files. A click can still submit a form: ask for what it should look up, not for something you can't undo.

You edit mcp.json yourself, since the tools leave it alone. Skills and routines can be written for you: ask any job, or run "onboard".

Usage cards

stats.json lists the cards in the left panel: claude and codex (usage windows), openrouter (credits, if you use it) and skills (runs this week). Add your own with any shell command; if it prints JSON like {"value": "3", "bars": [{"name": "done", "pct": 60}], "note": "..."} it becomes a card.

[
  { "type": "claude" }, { "type": "codex" }, { "type": "openrouter" }, { "type": "skills" },
  { "type": "command", "label": "Notes", "command": "git rev-list --count HEAD" }
]

Google Calendar, Tasks and Gmail (optional)

Lets every mode read and add events and to-dos, shown in the Agenda and Tasks panels, and read, search and draft your email, with your unread mail in the Mail panel (a click opens it in Gmail). Kryon never sends an email or deletes an event on its own: each draft it writes waits in Needs you as ✉ <recipients> · <subject>, and each event it wants to delete as 🗑 <title> · <start>. Read a draft in Gmail's Drafts first; Send on its line sends it, and Delete deletes the event. To say no, click ✕ (nothing happens). Other lines have Done. A draft you change in Gmail, or an event that moved since, isn't touched by the tick: send or delete it yourself. Kryon never deletes tasks or mail. One-time setup, about 5 minutes:

  1. At https://console.cloud.google.com, create a project.
  2. Enable the Calendar, Tasks and Gmail APIs in it.
  3. At https://console.cloud.google.com/auth/overview, click Get started: app name Kryon, your email, audience External, Create.
  4. Audience → Publish app (in "Testing", Google logs you out every 7 days).
  5. At https://console.cloud.google.com/auth/clients, Create client → Desktop app → Download JSON, and save it as kryon/.secrets/google-oauth.json.
  6. In kryon/app, run npm run google-login. Google warns the app isn't verified (it's yours: Advanced → continue); allow Tasks and Calendar, and Gmail if you want it (leave it unticked to keep Kryon out of your mail).

Restart Kryon. Several Google accounts? Add each one with a name of your choice: npm run google-login -- work (lowercase letters, digits, - and _), pick that account in the browser, and restart. The panels show every account together, the tools can pick one by name (the first login is called normal), and a draft from another account says so in its line ([draft:work:…]). Run the same command again to log an account back in.

Already set up before Gmail? Enable the Gmail API (step 2), run npm run google-login again and restart: Tasks and Calendar keep working until you do.

Saving tokens with Headroom (optional)

Headroom compresses Jev's and OpenCode's OpenRouter traffic before it reaches the models. OpenCode providers used directly keep their own connection:

pip install "headroom-ai[proxy]"

Add HEADROOM_URL=http://127.0.0.1:8787 to .env and run npm run autostart: the proxy then starts with Kryon. headroom dashboard shows what you saved.

On your phone (optional)

Kryon only listens on your computer (127.0.0.1). To reach it from your phone, use Tailscale on both, then on the computer:

tailscale serve --bg 5796

Open the https://<computer>.<tailnet>.ts.net address it prints on your phone, and add it to your home screen: it opens like an app. The microphone works since it's HTTPS. Anyone on your tailnet can use Kryon as you, so don't share that tailnet.

Notifications: with KRYON_NOTIFY_URL in .env, each new Needs you line (a draft to send, a heartbeat alert, an event to delete) is posted there, even one added while Kryon was off, as plain text, which ntfy shows on your phone. Use a long random topic (KRYON_NOTIFY_URL=https://ntfy.sh/kryon-<random>) or your own ntfy server, since the line can name a mail's subject. Set KRYON_URL to the Tailscale address and tapping the notification opens Kryon to tick it.

Known limits

  • Kryon can search and read the web in every mode: the sandbox protects your computer, not the internet. What it reads online can't write outside KryonData either. The one exception is a job that reads your mail (or another untrusted server): it has no web, so a mail can't trick it into sending your data somewhere. It can still add a calendar event, and an event with guests reaches them: keep an eye on events created from a mail.

  • Windows has no sandbox: in claude mode Kryon can edit files but not run shell commands; in opencode mode nothing stops it from writing outside KryonData.

  • Linux needs bubblewrap (bwrap) for the sandbox; without it opencode runs unsandboxed.

  • A job running when Kryon stops can't be resumed: it shows as failed. Conversations are kept.

  • Undo: with git installed, every job's changes to your notes and settings (text files in KryonData, not project repos or uploads) are kept in .kryon/history.git, and Undo its changes in History puts them back. It refuses when a later change touched the same lines. Jobs running at the same time share their changes.

Contributing

Coding agents (and humans) working on Kryon itself: start with AGENTS.md. Before opening a change, run cd app && npm run check && npm run build. Forgejo Actions runs the same checks on pushes and pull requests. Use Conventional Commits with one of these scopes: core, voice, ui, routines, stats, vault, template or docs.

Shipping

This repository includes Forgejo Actions workflows at .forgejo/workflows/ci.yml and .forgejo/workflows/release.yml. CI checks pushes and pull requests. Pushing a tag matching v*.*.* creates or updates a Forgejo release; users install its source archive as described above. The release workflow calls the Forgejo API directly, while CI uses pinned actions from GitHub by full URL. Both assume a runner with Docker and Node 22 support. If your instance blocks external actions, mirror the CI actions and replace those URLs with your Forgejo action paths.