Repository navigation
Releases: krowkcom/krowk
Release list
v0.14.0
Added
- An agent definition can name the MCP servers its subagent may use.
In itstools:list,mcp__githubgives the subagent the whole
githubserver andmcp__linear__create_issuethat one tool, as in
Claude Code;Mcp(github)andMcp(linear:create_issue)work too, and
mcp__*gives it every server. A name krowk can't read, such as the
patternmcp__github__get_*, is left out with a notice. Such
a subagent getsmcp_searchandmcp_callfor those alone: its
searches find nothing elsewhere, a call to any other server or tool is
refused before it runs, and a server it can't use isn't started for it.
Your permission rules and hooks still apply on top. These names used to
be dropped, so a definition with atools:line got no MCP at all; one
without atools:line still reaches every server. - A synced session goes direct on your LAN without Tailscale. With
KROWK_DIRECT_LAN=1, a host whose Tailscale isn't running now listens
on its own LAN addresses instead of leaving everything to the relay,
and advertises that listener over mDNS. A viewer on the same network
moves onto it, shown as "direct over LAN". When the host's LAN address
changes, say after a DHCP renewal or switching Wi-Fi, it listens on the
new address within a few seconds, and a viewer whose welcome named the
old one finds the new one over mDNS. Getting in still takes the
registry's ticket and your device's key, exactly as on the relay. The
mDNS name is a hash of the session id, so the network learns that a
krowk session is hosted at an address, never which one. A viewer only
queries mDNS when its host offered a LAN address, and only dials what it
finds there once the host's own addresses have failed, so a device
answering on someone else's behalf can't pull it off a working path.
Container and VM bridges (Docker, libvirt and the like) are not offered. Two machines behind
different NATs without Tailscale still go by the relay. - krowk tells your terminal whether it is working or waiting on you.
Beside the window title, the TUI now sends an OSC 7501 report (the
Program Status Protocol) each time its state changes: working while a
turn runs, blocked while an approval or the agent's questions wait on
you, idle otherwise. While working or blocked, its message is the prompt
as the title shows it (its first line, cut short).
Terminals and agent inboxes that read it, such as Rex, can list krowk
with its status without guessing from the title's glyphs. Leaving krowk,
stopping it with Ctrl-Z, or handing the terminal to a provider's own
sign-in under/connectclears what it reported, and krowk reports it
again once it has the terminal back. A terminal that doesn't know the
sequence ignores it. /connecttells your terminal it is waiting on your sign-in. While
its overlay is up, krowk reports blocked on auth (OSC 7501), with the
message "Sign in to" the provider you named or picked, and the window
title shows the same (inside herdr, krowk shows as blocked). An approval
that comes up meanwhile is reported instead until you answer it. Closing
or hiding the overlay, whatever came of it, reports what krowk is doing
otherwise; a provider's own sign-in that takes the terminal still clears
the record until krowk has it back.- The TUI leaves a turn's end on your terminal until you are back. A
terminal or agent inbox that reads OSC 7501 now shows a turn that
completed as done, with the prompt's first line, and one that failed as
failed, with the first line of what went wrong, rather than idle, so a
finished answer you haven't read stands apart from krowk at rest. Your
next key press puts it back to idle. An interrupted turn goes straight to
idle. The window title and herdr say idle as before. - Each subagent shows on your terminal as its own record. When a turn
fans out to subagents, the TUI reports each one beside krowk itself over
OSC 7501, titled by its description: working, waiting on an approval or
a question, done or failed. So a terminal that lists records can show
the one subagent that waits on you while the others work. A subagent you
stop is removed at once, and every subagent's record is removed when the
turn ends, before krowk reports it done or failed. krowk -pleaves a record of how it ended on your terminal. A
terminal that reads OSC 7501, the Program Status Protocol (Ghostty, Rex),
now shows akrowk -pyou started in another tab as working while its
turn runs, then done, failed (with the first line of what went wrong) or,
after Ctrl-C, idle, and keeps done or failed once krowk has exited. The
report goes to stderr, and only when stderr is a terminal: a stderr sent
to a file or a pipe gets nothing, and stdout is the same as before in
every--output-format. Other terminals ignore the sequence.- Background commands. The agent can run a command in the background
(bashwithrun_in_background) and keep working: a dev server, a long
test run, a build. It reads the output later withbash_outputand stops
the command withkill_bash. When one ends, the agent is told at its next
step. A command runs the way it would in the foreground, in the sandbox
when there is one. Up to 8 run at once per session, and they stop when
krowk's host does:krowk -p, and a TUI not on the host daemon, name
the ones they stop on the way out. On the daemon they outlive the krowk
that started them, and the daemon stays up until they end. The status line shows[N background]
while any run, and a second krowk following the session in the daemon
gets the count at once, not at the next change. A command's end, as the
agent is told it, is stored as krowk's in your session history, never as
a message of yours (and so is the todo reminder). - Background subagents. The agent can start a subagent in the
background (subagentwithrun_in_background) and go on working while
it runs. Its summary reaches the agent at its next step. The turn doesn't
finish while a background subagent still runs: the agent waits for it,
reads what it found, and answers. Interrupting the turn stops them too.
They count in[N background]. - Each subagent reports what it is doing as it goes. While a subagent
runs, krowk sends asubagent.statusline each time its state changes:
the tool it is in and since when, whether it waits on your approval or
answer, the tokens it has used, and how it ended. While it waits on you
it names no tool, and a tool you allow counts from your answer, not from
when it asked. Programs reading
--output-format stream-json, and anything attached to krowk's host,
get it without working it out from the raw lines; the TUI will show it
next. A subagent's start in the session log now also says who ran it
(ranBy); older logs read as krowk's own. - Groundwork for reading a subagent's transcript in the TUI. The view
that opens a subagent will keep its transcript's lines and scroll through
them itself: arrows, PgUp/PgDn, Home/End, following new lines until you
scroll up and again once you're back at the bottom. It keeps the last
20,000 lines and says when earlier ones are not shown. Nothing you see
changes yet: the main conversation is printed exactly as before. - A device attached to a synced session can stop one of its
subagents. A device following the session withkrowk sync attach
(and the apps, later) may now interrupt a subagent of that session by
the subagent's id, and only that subagent stops: the rest of the turn
goes on. It reaches only the subagents the session's own log started:
another session, an id the device made up, or steering a subagent is
refused as before. It works whichever directory the subagent's session
was started from. - The subagent view can take the whole screen and give it back. In the
inline screen it fills the terminal without switching to the alternate
screen, so tmux, SSH and phone terminals still get only plain output.
Nothing is printed into your scrollback while it is open, and when it
closes, or you quit krowk or suspend it with Ctrl-Z while it is open, the
conversation and the prompt are where they were. Lines that arrive in
the meantime are printed then. While an approval waits on you, the view
says so: in the inline screen Esc closes it to show the approval, and in
the fullscreen screen you answer it below as usual. In the fullscreen
screen it takes the conversation's area and leaves the prompt and status
line where they are, and the conversation comes back scrolled where you
left it. Resizing the window while it is open redraws it at the new size. One
limit in the inline screen: making the window narrower than the view's
lines pushes some of its rows into scrollback on terminals that reflow
(tmux among them). Enter on a subagent in the Ctrl-G overlay opens it. - Ctrl-G lists every subagent of the session, finished ones too. The
Agents overlay used to show only the subagents still running, so one that
had finished could not be found again. It now lists them all: the running
ones first, oldest first, then the finished ones, newest first, each
saying how it ended and how long it took (done in 42s,
interrupted after 3.1s,failed after 1m5s). A session you resume
lists its earlier subagents the same way; one its log left without an
answer is listed as interrupted, not as still running. The selection
stays on the subagent you picked as others start and finish, andxon
a finished one says there is nothing to interrupt. The area above the prompt still shows
only the subagents running now, and a subagent line in the conversation
now says how long an interrupted or failed one ran, also in a resumed
session, where it used to sayrunning 0.0s. - **A running subagent's line says whether it is busy, ...
v0.13.0
Added
-
A subagent can work in a git worktree of its own. Give the
subagenttoolisolation: "worktree", or putisolation: worktreein
an agent definition's frontmatter as in Claude Code's agent files (the
call's value wins), and the subagent runs in a new worktree of your
repository, on a branchkrowk/<8 hex>, under
~/.local/share/krowk/worktrees(or$XDG_DATA_HOME/krowk/worktrees).
Subagents started together no longer overwrite each other's files. The
worktree starts from your files as they are, uncommitted changes
included: modified, new and deleted files, and what you staged (ignored
files only if you force-added them, nested repositories never), are put
in one commit,krowk: working state for <hex>, on top of yourHEAD, and
the branch starts there; with nothing uncommitted it starts atHEAD.
Your index,HEADand files are not touched. When
it finishes having changed nothing, the worktree and its branch are
removed. When it changed something, its changes are applied to your
working tree (see below). Outside a git repository the call fails
withisolation: worktree needs a git repository. Without the field, or
with"none", a subagent runs in your directory as before. Your
repository's git hooks do not run when the worktree is made. -
A subagent's work in its worktree comes back to your working tree.
When a subagent withisolation: "worktree"finishes having changed
something, krowk applies its commits and uncommitted changes, new,
deleted and binary files included, to the working tree the parent
agent works in, as uncommitted changes: your index andHEADare not
touched. The worktree and its branch are then removed, its final state
kept asrefs/krowk/snapshots/<hex>for 30 days, and the summary the
parent gets ends withChanges applied to your working tree: <files>.
Subagents finishing at once apply one after another. When a file
doesn't apply cleanly (you, or a sibling, changed the same lines),
nothing of it is applied, the worktree and branch are kept, and the
summary ends withChanges not applied (conflicts in <files>). Worktree: <path>, branch krowk/<hex>. A subagent that changed a
submodule is kept the same way, as krowk doesn't apply submodule
changes. So is one that changed files inside a.git,.claude,
.codexor.krowkdirectory, which the file tools only change with
your say: its summary ends withChanges not applied (they touch protected files: <files>), the worktree and branch, and the
krowk worktrees apply <hex>you run to apply them.
krowk worktrees apply <hex|path> [--to <dir>]does the same for any
kept worktree, protected files included, into your repository's main
checkout by default: it is how you bring a--worktreesession's work
home. It refuses (exit 4) while that session is still running, or when
a file conflicts, and refuses (exit 1) a--tothat is a checkout of
another repository. -
A subagent's worktree has your submodules checked out. Before the
subagent starts, krowk initialises every submodule of its worktree,
submodules inside submodules too, at the commits the worktree records.
One you have initialised in your main checkout is copied from there,
with no download; itsoriginstays its own URL. One you haven't is
cloned from its URL, with no terminal to prompt on and a 2-minute limit.
A submodule that can't be initialised stays empty, and the subagent's
first prompt says which and what git said. URLs from.gitmodulesthat
would run a command (ext::) or copy a repository from a local path are
refused. Your repository's config is not changed, so a submodule you
deinitialised stays that way in your checkout. The agent can edit files
in a submodule but not commit in it. An edit or a new file in any
submodule, at any depth, keeps the worktree; an unchanged one is removed
with its submodules' git data. -
A subagent's worktree starts with your build output. On a file
system that can clone files without copying their blocks (btrfs, XFS
with reflink, APFS on macOS), krowk clones your main checkout'starget
andnode_modulesinto a new worktree before the subagent starts, so its
firstcargo buildfinds nothing to compile and the clone takes almost
no disk.targetis skipped while a build is running in it, and
node_modulesunless the worktree's lockfile (package-lock.json,
pnpm-lock.yaml,yarn.lock,bun.lockorbun.lockb) is the same as
your checkout's. Anywhere else, and on Windows, nothing is copied. Choose
the directories withworktrees.seedinconfig.json, for example
{"worktrees": {"seed": ["target", ".venv"]}};[]turns it off.
What was skipped and why is inseed.log, beside the repository's
worktrees. -
Worktrees appear almost instantly on btrfs. When krowk's worktrees
directory is on btrfs, on the same file system as your repository, and
thebtrfsprogram is installed, a new worktree for a subagent or a
krowk --worktreesession is a snapshot of a copy of your source tree
that krowk keeps up to date, with your build output already in it:
about a tenth of a second whatever the repository's size, and its first
cargo buildcompiles only what differs from your last build. Twenty
agents starting at once in one repository each get theirs in under half
a second, rather than waiting on each other. Anywhere else, or if a
snapshot fails, the worktree is checked out as before. -
A subagent's worktree gets the ignored files you list in
.worktreeinclude. As in Claude Code, put a.worktreeincludeat the
top of your main checkout, in.gitignoresyntax (.env*,
config/local.yml), and every file it matches that git ignores is
copied into a new worktree before the subagent starts, with its mode.
They are copies, never links, so the agent can't change your real
.env; on a file system that clones files they cost no disk. Tracked
files are never copied over the worktree's, nothing is read through a
symlink or written over a file the worktree has, and the
worktrees.seeddirectories are left to seeding. A file that can't be
copied is skipped and noted inseed.log. Without a
.worktreeinclude, nothing is copied. -
A subagent's worktree can run your project's setup command first.
Setworktrees.setupto a shell command (npm ci, a code generator,
a database script) and krowk runs it in each new worktree before the
subagent starts, so the agent doesn't burn turns finding out the
project isn't installed. Put it in~/.krowk/config.jsonfor yourself,
or in a repository's.krowk/config.json, where it runs only once you
have trusted the repository (the same question that turns its hooks
on); a trusted repository's command is the one that runs. It runs in
the sandbox: network on, the worktree writable, your main checkout and
your home not, so a cloned repository's install can't touch anything
else. On a machine with no sandbox it runs only for a trusted
repository. It getsKROWK_PROJECT_ROOT(your main checkout),
KROWK_WORKTREE_PATHandKROWK_PORT_BASE: each live worktree holds
its own ten ports from 20000 up (20000, 20010, …), and the subagent's
own commands seeKROWK_PORT_BASEtoo, so dev servers in two worktrees
don't collide. A build or install command waits for a build slot as the
agent's would. Its output is inkrowk-setup.login the worktree's git
directory. When it fails, or runs pastworktrees.setupTimeout(600
seconds by default), the subagent starts anyway and its first prompt
says so, with the exit code or "timed out" and the last 50 lines of
output. -
krowk worktreeslists, removes and prunes the worktrees krowk
kept.krowk worktreesshows every one, across repositories: its
path, repository, branch, base commit, commits ahead of the base,
whether it has uncommitted changes, the session that made it, its age,
and whether that session is still running (--jsonfor scripts).
krowk worktrees remove <hex|path>removes one, and keeps its branch.
It refuses while a running session uses it, and refuses one with
uncommitted changes or commits ahead of its base unless you add
--force. A forced removal first saves the uncommitted changes, new
files included, asrefs/krowk/snapshots/<hex>, and aHEADits
branch doesn't hold (the agent detached it, or switched branch, and
committed) asrefs/krowk/snapshots/<hex>-head, and prints the commands
that bring the worktree back. Ignored files, such as build output and
the copies.worktreeincludemade, don't count as changes and are
deleted with it.krowk worktrees pruneclears git's record of krowk's
worktrees whose directory you deleted (only krowk's: your own
worktrees' records are left alone, even when their drive isn't
mounted), deletes worktree directories git no longer knows when their
files match their branch (one with changes is left in place and
named), and deletes snapshots older than 30 days. A repository that has
moved, or whose drive isn't mounted, is skipped and named. Prune also
runs by itself in the background, at most once a day, when a session
starts. Worktrees made by an earlier krowk are
listed too, with their base shown as unknown, so removing one of them
needs--force. -
krowk --worktreeandkrowk -p --worktreestart a session in a git
worktree of its own. Sessions you start in separate terminals no
longer share one checkout. krowk makes the worktree from the repository
you are in, your uncommitted changes included, on a branch
krowk/<8 hex>, readies it as it does a subagent's (submodules, build
output,.worktreeinclude,worktrees.setup), and starts the session
there; the TUI's header shows the branch, and the agent's commands get
KROWK_PORT_BASE. When the session ends ha...
v0.12.1
Changed
- krowk says what it is: a coding agent harness.
krowk help, the
installer and the package descriptions lead with one session on any
machine, model or agent; publishing to a permalink is one of the things it
does.syncanddevicesmove up to theAGENTheading inkrowk help.
The lean build, which only publishes, still calls itself that. - The installer writes the agent skill for every agent that reads one.
Besides Claude Code's~/.claude/skills, it writes~/.agents/skills,
which krowk and Codex read, when that directory exists, and names every
place it wrote on one line. Its next steps lead withkrowk,krowk login
andkrowk sync init, thenkrowk push.
Install
# npm — the bare `krowk` name is blocked by npm's typosquat filter
npx @krowk/cli push screenshot.png
# The installer: the full build on a workstation, the lean one in CI
# and containers (or with --lean)
curl -fsSL https://krowk.com/install | bash
# Direct download — no runtime, no package manager. krowk_… is the
# full build (the agent, its TUI, sessions); krowk-lean_… the lean
# agent-container build.
curl -sSfL https://github.com/krowkcom/krowk/releases/download/v0.12.1/krowk_0.12.1_$(uname -s | tr A-Z a-z)_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz \
| tar -xz -C /usr/local/bin krowk krowk-mcp
# From source
cargo install --locked --git https://github.com/krowkcom/krowk --tag v0.12.1 --features harness krowkchecksums.txt covers every archive on this release, and
checksums.txt.sigstore.json is its signature. To verify a
download (with cosign):
cosign verify-blob checksums.txt --bundle checksums.txt.sigstore.json \
--certificate-identity https://github.com/krowkcom/krowk/.github/workflows/release.yml@refs/tags/v0.12.1 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum --check --ignore-missing checksums.txtThe .cdx.json files are CycloneDX SBOMs of the full and lean
builds, each signed the same way. SECURITY.md in the repository says
how to rebuild a release and compare the bytes; this release's
reproduce job already did it once for Linux x86-64.
v0.12.0
Added
-
krowk sync attachopens the TUI. On a terminal, a session another
machine runs is drawn as your own are: its history, then live. Prompts go
from the prompt box, approvals are answered in the usual dialog, and Esc
interrupts. The status line says whether the host is there (prompts wait
for it when it is not) and whether the session comes by the relay or
directly.krowk --resume <id>of a synced session opens it the same way.
The host runs prompts from a viewer in the session's default mode, so
/mode,/model,/newand/sessionssay they are the host's, and a
viewer allows a call once or for the session, never for the host's
project. With--json, or stdout not a terminal, it prints stream-json as
before. -
krowk sync initsets up your device list, with a 12-word recovery
kit. It asks you to sign in again in the browser, then makes the list's
first entry: this device and the recovery device the kit derives, with your
user key wrapped to both. The kit's words are shown once on stderr ([Enter]
when written down, [s] to skip) or written to--save FILE, 0600, and are
never typed back.--start-overmakes a new list; every other device on
the old one stops syncing and asks to be paired again. Run from a device
that holds your key, it keeps the old keys aside and seals the sessions it
can open again under the new list; running it again (under another
workspace's key, for that workspace's) goes through what is left, and
krowk sync recovery discard-olddrops the old keys when you say so. The 24-word recovery phrase is gone, and so is
krowk sync register: a device is registered by being on the list. -
krowk devices remove NAMEtakes a device off your list and rotates
your key away from it. It names every device the new key goes to before it
asks, needs a fresh sign-in, and removes any device revoked on the dashboard
in the same post, so no new key reaches one.krowk sync statusoffers to
finish a dashboard Revoke. Your other devices take the new key at their next
sync. -
krowk sync recovergets back in on a new machine from the kit's
words, typed at a prompt that doesn't echo them or piped in. It verifies
the list from its first entry, goes through every device on it with you to
keep or remove, and only then wraps your key, to what you kept. -
krowk sync recovery newreplaces the kit at once, with the old kit's
words, or from any device when you have none.krowk sync recovery checktests the words against the list, locally.krowk sync status
verifies the list against the head this device pinned, and says when there
is no kit; so does the TUI's status line. -
Pairing a device by a short code, in the library.
krowk_client::pairing
holds both sides ofkrowk devices addas sans-IO state machines: an
eight-character code (Crockford base32 less0and1, shownXXXX-XXXX,
typed in any case with spaces and dashes ignored), SPAKE2 in asymmetric mode
bound to the peer kind, the person and both device ids, and key confirmation
both ways before the new device's name is shown or anything is posted. One
failed step ends the pairing, and the new device's code is consumed by the
attempt, so a hostile registry gets one guess against each side. The SPAKE2 crate is held to magic-wormhole's vectors. Nothing
calls it yet. -
The client crypto for devices you own, not yet wired to any command:
a user key per person with generations, each wrapping the one before; a
12-word recovery kit that derives a recovery device; and a signed,
chained device list that every client verifies against the head it last
saw, refusing an older or forked list and any removal of the recovery
device. Only the current kit can replace the kit, or any device when there
is none. -
krowk devices addandkrowk sync joinpair a machine by a short
code.addshowsXXXX-XXXX, valid ten minutes and once;joinon the
new machine takes it at a prompt, never as an argument. Both machines check
the code, the paired one asksAdd '<name>' (<os>) to your devices? [Y/n],
and the new machine keeps the user key only once the chain it was sent
adds exactly its keys. A wrong code, or any failure on the new machine, a
dropped connection included, ends the pairing and asks for a new code. A
machine that was sent the key but never confirmed it is still listed, so
krowk devices removecan take it off; ^C onaddends the pairing. -
krowk sync hostseals under the device list as the registry has it
now, extended from this machine's pin, and refuses when the registry
cannot be asked, rather than sealing under a list a removal left behind. -
The stand-in registry holds devices you own.
krowk-devregistryserves
a person's signed device list (verified on every post by krowk-client's own
verifier), the user key wrapped to each device, keys bound to one device,
a fresh sign-in stamp, and the pairing mailbox: one live pairing per
person, ten minutes, ended by the first step out of turn. The device
approval endpoints answer410 sync_reset. -
Releases are signed, come with an SBOM, and are proven reproducible.
Each release carrieschecksums.txt.sigstore.json, a keyless Sigstore
signature over the checksums of every archive, bound to the release
workflow at that tag, and a CycloneDX SBOM of the full and lean builds,
each signed the same way. Before anything is published, the release builds
Linux x86-64 a second time on another runner from another path and stops
unless every binary matches byte for byte. The npm packages carry npm
provenance.SECURITY.mdsays how to verify a download and rebuild one. -
SECURITY.md: where to report a vulnerability (security@krowk.com),
what happens then, what is in scope, and every network call krowk makes
without being asked. krowk has no telemetry and no crash reporting. -
cargo denyon every pull request and at every tag. A dependency
with a RustSec advisory, a licence outsidedeny.toml's list, or a
source other than crates.io fails CI and stops a release. -
krowk sync attachanswers approvals and steers the turn. Besides
prompts, a line typed on its stdin can be/approve REQUEST_ID,
/allow-session REQUEST_IDor/deny REQUEST_ID, answering the tool call
anapproval.requestedline names,/interrupt, which stops the running
turn, or/steer TEXT, which adds to it. Eachapproval.requestedline
carries itsrequestId, and stderr says which command answers it. Any
other line starting with/goes as a prompt, so skills and paths still
work, and//TEXTsends/TEXT.krowk help sync attachlists them. -
A synced session goes direct over Tailscale when it can.
krowk sync hostreads this machine's tailnet address, MagicDNS name and LAN address
from the localtailscaled, listens there, and names the addresses to
each viewer inside the sealed channel. A viewer tries them all beside the
relay and moves onto the first that passes the same ticket, challenge and
handshake the relay does, mid-session and without losing a frame; if the
direct path goes, or its welcome is more than three seconds late, it
falls back to the relay by itself and tries again later, less often each
time nothing is found (10 seconds, doubling to 5 minutes).krowk sync attachprints the path in use (sync.path:relay,
direct over Tailscaleordirect over LAN). Being on the tailnet lets
nobody in: direct paths are on only with the registry's ticket keys in
KROWK_RELAY_TICKET_KEYS, andKROWK_TAILSCALE_SAME_USER=1also turns
away a connection from another tailnet user. The LAN address is offered
only withKROWK_DIRECT_LAN=1, and never with the same-user check.
krowk hostslists the
tailnet's machines taggedtag:krowk-host. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
A session running on one machine can be watched and steered from
another. The host keeps the session's lease, renewing it every 20
seconds, streams it over the relay sealed end to end, and writes its log
to the registry as sealed chunks with a checkpoint to attach from.
Another of your machines attaches from the latest checkpoint in well
under half a second, follows the session live, and can prompt it, steer
or stop it, and answer its approvals. When the host is away the other
machine is read-only, and a prompt typed there waits and runs when the
host is back. A dropped connection resumes by itself with nothing lost.
Neither the relay nor the registry ever sees the session's content.
krowk sync host <session>syncs a session of this machine,krowk sync sessionslists what another machine can open, andkrowk sync attach <session>— orkrowk --resume <session>on a machine without the
session's log — follows it there. The relay isKROWK_RELAY_URL, or
krowk relay serveon this machine. -
Sync calls that act as this machine are signed by its own key.
Registering or approving a device, a lease call, writing a session or
its log, and asking for a relay ticket now carryX-Krowk-Device,
X-Krowk-TimestampandX-Krowk-Signature, an Ed25519 signature by
this machine's signing key. The registry refuses them unsigned, signed by
another key, more than five minutes off its clock, or sent twi...
v0.12.0-rc7
Added
-
krowk sync attachopens the TUI. On a terminal, a session another
machine runs is drawn as your own are: its history, then live. Prompts go
from the prompt box, approvals are answered in the usual dialog, and Esc
interrupts. The status line says whether the host is there (prompts wait
for it when it is not) and whether the session comes by the relay or
directly.krowk --resume <id>of a synced session opens it the same way.
The host runs prompts from a viewer in the session's default mode, so
/mode,/model,/newand/sessionssay they are the host's, and a
viewer allows a call once or for the session, never for the host's
project. With--json, or stdout not a terminal, it prints stream-json as
before. -
krowk sync initsets up your device list, with a 12-word recovery
kit. It asks you to sign in again in the browser, then makes the list's
first entry: this device and the recovery device the kit derives, with your
user key wrapped to both. The kit's words are shown once on stderr ([Enter]
when written down, [s] to skip) or written to--save FILE, 0600, and are
never typed back.--start-overmakes a new list; every other device on
the old one stops syncing and asks to be paired again. Run from a device
that holds your key, it keeps the old keys aside and seals the sessions it
can open again under the new list; running it again (under another
workspace's key, for that workspace's) goes through what is left, and
krowk sync recovery discard-olddrops the old keys when you say so. The 24-word recovery phrase is gone, and so is
krowk sync register: a device is registered by being on the list. -
krowk devices remove NAMEtakes a device off your list and rotates
your key away from it. It names every device the new key goes to before it
asks, needs a fresh sign-in, and removes any device revoked on the dashboard
in the same post, so no new key reaches one.krowk sync statusoffers to
finish a dashboard Revoke. Your other devices take the new key at their next
sync. -
krowk sync recovergets back in on a new machine from the kit's
words, typed at a prompt that doesn't echo them or piped in. It verifies
the list from its first entry, goes through every device on it with you to
keep or remove, and only then wraps your key, to what you kept. -
krowk sync recovery newreplaces the kit at once, with the old kit's
words, or from any device when you have none.krowk sync recovery checktests the words against the list, locally.krowk sync status
verifies the list against the head this device pinned, and says when there
is no kit; so does the TUI's status line. -
Pairing a device by a short code, in the library.
krowk_client::pairing
holds both sides ofkrowk devices addas sans-IO state machines: an
eight-character code (Crockford base32 less0and1, shownXXXX-XXXX,
typed in any case with spaces and dashes ignored), SPAKE2 in asymmetric mode
bound to the peer kind, the person and both device ids, and key confirmation
both ways before the new device's name is shown or anything is posted. One
failed step ends the pairing, and the new device's code is consumed by the
attempt, so a hostile registry gets one guess against each side. The SPAKE2 crate is held to magic-wormhole's vectors. Nothing
calls it yet. -
The client crypto for devices you own, not yet wired to any command:
a user key per person with generations, each wrapping the one before; a
12-word recovery kit that derives a recovery device; and a signed,
chained device list that every client verifies against the head it last
saw, refusing an older or forked list and any removal of the recovery
device. Only the current kit can replace the kit, or any device when there
is none. -
krowk devices addandkrowk sync joinpair a machine by a short
code.addshowsXXXX-XXXX, valid ten minutes and once;joinon the
new machine takes it at a prompt, never as an argument. Both machines check
the code, the paired one asksAdd '<name>' (<os>) to your devices? [Y/n],
and the new machine keeps the user key only once the chain it was sent
adds exactly its keys. A wrong code, or any failure on the new machine, a
dropped connection included, ends the pairing and asks for a new code. A
machine that was sent the key but never confirmed it is still listed, so
krowk devices removecan take it off; ^C onaddends the pairing. -
krowk sync hostseals under the device list as the registry has it
now, extended from this machine's pin, and refuses when the registry
cannot be asked, rather than sealing under a list a removal left behind. -
The stand-in registry holds devices you own.
krowk-devregistryserves
a person's signed device list (verified on every post by krowk-client's own
verifier), the user key wrapped to each device, keys bound to one device,
a fresh sign-in stamp, and the pairing mailbox: one live pairing per
person, ten minutes, ended by the first step out of turn. The device
approval endpoints answer410 sync_reset. -
Releases are signed, come with an SBOM, and are proven reproducible.
Each release carrieschecksums.txt.sigstore.json, a keyless Sigstore
signature over the checksums of every archive, bound to the release
workflow at that tag, and a CycloneDX SBOM of the full and lean builds,
each signed the same way. Before anything is published, the release builds
Linux x86-64 a second time on another runner from another path and stops
unless every binary matches byte for byte. The npm packages carry npm
provenance.SECURITY.mdsays how to verify a download and rebuild one. -
SECURITY.md: where to report a vulnerability (security@krowk.com),
what happens then, what is in scope, and every network call krowk makes
without being asked. krowk has no telemetry and no crash reporting. -
cargo denyon every pull request and at every tag. A dependency
with a RustSec advisory, a licence outsidedeny.toml's list, or a
source other than crates.io fails CI and stops a release. -
krowk sync attachanswers approvals and steers the turn. Besides
prompts, a line typed on its stdin can be/approve REQUEST_ID,
/allow-session REQUEST_IDor/deny REQUEST_ID, answering the tool call
anapproval.requestedline names,/interrupt, which stops the running
turn, or/steer TEXT, which adds to it. Eachapproval.requestedline
carries itsrequestId, and stderr says which command answers it. Any
other line starting with/goes as a prompt, so skills and paths still
work, and//TEXTsends/TEXT.krowk help sync attachlists them. -
A synced session goes direct over Tailscale when it can.
krowk sync hostreads this machine's tailnet address, MagicDNS name and LAN address
from the localtailscaled, listens there, and names the addresses to
each viewer inside the sealed channel. A viewer tries them all beside the
relay and moves onto the first that passes the same ticket, challenge and
handshake the relay does, mid-session and without losing a frame; if the
direct path goes, or its welcome is more than three seconds late, it
falls back to the relay by itself and tries again later, less often each
time nothing is found (10 seconds, doubling to 5 minutes).krowk sync attachprints the path in use (sync.path:relay,
direct over Tailscaleordirect over LAN). Being on the tailnet lets
nobody in: direct paths are on only with the registry's ticket keys in
KROWK_RELAY_TICKET_KEYS, andKROWK_TAILSCALE_SAME_USER=1also turns
away a connection from another tailnet user. The LAN address is offered
only withKROWK_DIRECT_LAN=1, and never with the same-user check.
krowk hostslists the
tailnet's machines taggedtag:krowk-host. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
A session running on one machine can be watched and steered from
another. The host keeps the session's lease, renewing it every 20
seconds, streams it over the relay sealed end to end, and writes its log
to the registry as sealed chunks with a checkpoint to attach from.
Another of your machines attaches from the latest checkpoint in well
under half a second, follows the session live, and can prompt it, steer
or stop it, and answer its approvals. When the host is away the other
machine is read-only, and a prompt typed there waits and runs when the
host is back. A dropped connection resumes by itself with nothing lost.
Neither the relay nor the registry ever sees the session's content.
krowk sync host <session>syncs a session of this machine,krowk sync sessionslists what another machine can open, andkrowk sync attach <session>— orkrowk --resume <session>on a machine without the
session's log — follows it there. The relay isKROWK_RELAY_URL, or
krowk relay serveon this machine. -
Sync calls that act as this machine are signed by its own key.
Registering or approving a device, a lease call, writing a session or
its log, and asking for a relay ticket now carryX-Krowk-Device,
X-Krowk-TimestampandX-Krowk-Signature, an Ed25519 signature by
this machine's signing key. The registry refuses them unsigned, signed by
another key, more than five minutes off its clock, or sent twi...
v0.12.0-rc6
Added
-
krowk sync initsets up your device list, with a 12-word recovery
kit. It asks you to sign in again in the browser, then makes the list's
first entry: this device and the recovery device the kit derives, with your
user key wrapped to both. The kit's words are shown once on stderr ([Enter]
when written down, [s] to skip) or written to--save FILE, 0600, and are
never typed back.--start-overmakes a new list; every other device on
the old one stops syncing and asks to be paired again. Run from a device
that holds your key, it keeps the old keys aside and seals the sessions it
can open again under the new list; running it again (under another
workspace's key, for that workspace's) goes through what is left, and
krowk sync recovery discard-olddrops the old keys when you say so. The 24-word recovery phrase is gone, and so is
krowk sync register: a device is registered by being on the list. -
krowk devices remove NAMEtakes a device off your list and rotates
your key away from it. It names every device the new key goes to before it
asks, needs a fresh sign-in, and removes any device revoked on the dashboard
in the same post, so no new key reaches one.krowk sync statusoffers to
finish a dashboard Revoke. Your other devices take the new key at their next
sync. -
krowk sync recovergets back in on a new machine from the kit's
words, typed at a prompt that doesn't echo them or piped in. It verifies
the list from its first entry, goes through every device on it with you to
keep or remove, and only then wraps your key, to what you kept. -
krowk sync recovery newreplaces the kit at once, with the old kit's
words, or from any device when you have none.krowk sync recovery checktests the words against the list, locally.krowk sync status
verifies the list against the head this device pinned, and says when there
is no kit; so does the TUI's status line. -
Pairing a device by a short code, in the library.
krowk_client::pairing
holds both sides ofkrowk devices addas sans-IO state machines: an
eight-character code (Crockford base32 less0and1, shownXXXX-XXXX,
typed in any case with spaces and dashes ignored), SPAKE2 in asymmetric mode
bound to the peer kind, the person and both device ids, and key confirmation
both ways before the new device's name is shown or anything is posted. One
failed step ends the pairing, and the new device's code is consumed by the
attempt, so a hostile registry gets one guess against each side. The SPAKE2 crate is held to magic-wormhole's vectors. Nothing
calls it yet. -
The client crypto for devices you own, not yet wired to any command:
a user key per person with generations, each wrapping the one before; a
12-word recovery kit that derives a recovery device; and a signed,
chained device list that every client verifies against the head it last
saw, refusing an older or forked list and any removal of the recovery
device. Only the current kit can replace the kit, or any device when there
is none. -
krowk devices addandkrowk sync joinpair a machine by a short
code.addshowsXXXX-XXXX, valid ten minutes and once;joinon the
new machine takes it at a prompt, never as an argument. Both machines check
the code, the paired one asksAdd '<name>' (<os>) to your devices? [Y/n],
and the new machine keeps the user key only once the chain it was sent
adds exactly its keys. A wrong code, or any failure on the new machine, a
dropped connection included, ends the pairing and asks for a new code. A
machine that was sent the key but never confirmed it is still listed, so
krowk devices removecan take it off; ^C onaddends the pairing. -
krowk sync hostseals under the device list as the registry has it
now, extended from this machine's pin, and refuses when the registry
cannot be asked, rather than sealing under a list a removal left behind. -
The stand-in registry holds devices you own.
krowk-devregistryserves
a person's signed device list (verified on every post by krowk-client's own
verifier), the user key wrapped to each device, keys bound to one device,
a fresh sign-in stamp, and the pairing mailbox: one live pairing per
person, ten minutes, ended by the first step out of turn. The device
approval endpoints answer410 sync_reset. -
Releases are signed, come with an SBOM, and are proven reproducible.
Each release carrieschecksums.txt.sigstore.json, a keyless Sigstore
signature over the checksums of every archive, bound to the release
workflow at that tag, and a CycloneDX SBOM of the full and lean builds,
each signed the same way. Before anything is published, the release builds
Linux x86-64 a second time on another runner from another path and stops
unless every binary matches byte for byte. The npm packages carry npm
provenance.SECURITY.mdsays how to verify a download and rebuild one. -
SECURITY.md: where to report a vulnerability (security@krowk.com),
what happens then, what is in scope, and every network call krowk makes
without being asked. krowk has no telemetry and no crash reporting. -
cargo denyon every pull request and at every tag. A dependency
with a RustSec advisory, a licence outsidedeny.toml's list, or a
source other than crates.io fails CI and stops a release. -
krowk sync attachanswers approvals and steers the turn. Besides
prompts, a line typed on its stdin can be/approve REQUEST_ID,
/allow-session REQUEST_IDor/deny REQUEST_ID, answering the tool call
anapproval.requestedline names,/interrupt, which stops the running
turn, or/steer TEXT, which adds to it. Eachapproval.requestedline
carries itsrequestId, and stderr says which command answers it. Any
other line starting with/goes as a prompt, so skills and paths still
work, and//TEXTsends/TEXT.krowk help sync attachlists them. -
A synced session goes direct over Tailscale when it can.
krowk sync hostreads this machine's tailnet address, MagicDNS name and LAN address
from the localtailscaled, listens there, and names the addresses to
each viewer inside the sealed channel. A viewer tries them all beside the
relay and moves onto the first that passes the same ticket, challenge and
handshake the relay does, mid-session and without losing a frame; if the
direct path goes, or its welcome is more than three seconds late, it
falls back to the relay by itself and tries again later, less often each
time nothing is found (10 seconds, doubling to 5 minutes).krowk sync attachprints the path in use (sync.path:relay,
direct over Tailscaleordirect over LAN). Being on the tailnet lets
nobody in: direct paths are on only with the registry's ticket keys in
KROWK_RELAY_TICKET_KEYS, andKROWK_TAILSCALE_SAME_USER=1also turns
away a connection from another tailnet user. The LAN address is offered
only withKROWK_DIRECT_LAN=1, and never with the same-user check.
krowk hostslists the
tailnet's machines taggedtag:krowk-host. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
A session running on one machine can be watched and steered from
another. The host keeps the session's lease, renewing it every 20
seconds, streams it over the relay sealed end to end, and writes its log
to the registry as sealed chunks with a checkpoint to attach from.
Another of your machines attaches from the latest checkpoint in well
under half a second, follows the session live, and can prompt it, steer
or stop it, and answer its approvals. When the host is away the other
machine is read-only, and a prompt typed there waits and runs when the
host is back. A dropped connection resumes by itself with nothing lost.
Neither the relay nor the registry ever sees the session's content.
krowk sync host <session>syncs a session of this machine,krowk sync sessionslists what another machine can open, andkrowk sync attach <session>— orkrowk --resume <session>on a machine without the
session's log — follows it there. The relay isKROWK_RELAY_URL, or
krowk relay serveon this machine. -
Sync calls that act as this machine are signed by its own key.
Registering or approving a device, a lease call, writing a session or
its log, and asking for a relay ticket now carryX-Krowk-Device,
X-Krowk-TimestampandX-Krowk-Signature, an Ed25519 signature by
this machine's signing key. The registry refuses them unsigned, signed by
another key, more than five minutes off its clock, or sent twice, so a
workspace API key alone can no longer act as one of its devices. A
clock more than five minutes out makes these calls fail with
signature_stale. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
**Setting up ...
v0.12.0-rc5
Added
-
krowk sync initsets up your device list, with a 12-word recovery
kit. It asks you to sign in again in the browser, then makes the list's
first entry: this device and the recovery device the kit derives, with your
user key wrapped to both. The kit's words are shown once on stderr ([Enter]
when written down, [s] to skip) or written to--save FILE, 0600, and are
never typed back.--start-overmakes a new list; every other device on
the old one stops syncing and asks to be paired again. Run from a device
that holds your key, it keeps the old keys aside and seals the sessions it
can open again under the new list; running it again (under another
workspace's key, for that workspace's) goes through what is left, and
krowk sync recovery discard-olddrops the old keys when you say so. The 24-word recovery phrase is gone, and so is
krowk sync register: a device is registered by being on the list. -
krowk devices remove NAMEtakes a device off your list and rotates
your key away from it. It names every device the new key goes to before it
asks, needs a fresh sign-in, and removes any device revoked on the dashboard
in the same post, so no new key reaches one.krowk sync statusoffers to
finish a dashboard Revoke. Your other devices take the new key at their next
sync. -
krowk sync recovergets back in on a new machine from the kit's
words, typed at a prompt that doesn't echo them or piped in. It verifies
the list from its first entry, goes through every device on it with you to
keep or remove, and only then wraps your key, to what you kept. -
krowk sync recovery newreplaces the kit at once, with the old kit's
words, or from any device when you have none.krowk sync recovery checktests the words against the list, locally.krowk sync status
verifies the list against the head this device pinned, and says when there
is no kit; so does the TUI's status line. -
Pairing a device by a short code, in the library.
krowk_client::pairing
holds both sides ofkrowk devices addas sans-IO state machines: an
eight-character code (Crockford base32 less0and1, shownXXXX-XXXX,
typed in any case with spaces and dashes ignored), SPAKE2 in asymmetric mode
bound to the peer kind, the person and both device ids, and key confirmation
both ways before the new device's name is shown or anything is posted. One
failed step ends the pairing, and the new device's code is consumed by the
attempt, so a hostile registry gets one guess against each side. The SPAKE2 crate is held to magic-wormhole's vectors. Nothing
calls it yet. -
The client crypto for devices you own, not yet wired to any command:
a user key per person with generations, each wrapping the one before; a
12-word recovery kit that derives a recovery device; and a signed,
chained device list that every client verifies against the head it last
saw, refusing an older or forked list and any removal of the recovery
device. Only the current kit can replace the kit, or any device when there
is none. -
krowk devices addandkrowk sync joinpair a machine by a short
code.addshowsXXXX-XXXX, valid ten minutes and once;joinon the
new machine takes it at a prompt, never as an argument. Both machines check
the code, the paired one asksAdd '<name>' (<os>) to your devices? [Y/n],
and the new machine keeps the user key only once the chain it was sent
adds exactly its keys. A wrong code, or any failure on the new machine, a
dropped connection included, ends the pairing and asks for a new code. A
machine that was sent the key but never confirmed it is still listed, so
krowk devices removecan take it off; ^C onaddends the pairing. -
krowk sync hostseals under the device list as the registry has it
now, extended from this machine's pin, and refuses when the registry
cannot be asked, rather than sealing under a list a removal left behind. -
The stand-in registry holds devices you own.
krowk-devregistryserves
a person's signed device list (verified on every post by krowk-client's own
verifier), the user key wrapped to each device, keys bound to one device,
a fresh sign-in stamp, and the pairing mailbox: one live pairing per
person, ten minutes, ended by the first step out of turn. The device
approval endpoints answer410 sync_reset. -
Releases are signed, come with an SBOM, and are proven reproducible.
Each release carrieschecksums.txt.sigstore.json, a keyless Sigstore
signature over the checksums of every archive, bound to the release
workflow at that tag, and a CycloneDX SBOM of the full and lean builds,
each signed the same way. Before anything is published, the release builds
Linux x86-64 a second time on another runner from another path and stops
unless every binary matches byte for byte. The npm packages carry npm
provenance.SECURITY.mdsays how to verify a download and rebuild one. -
SECURITY.md: where to report a vulnerability (security@krowk.com),
what happens then, what is in scope, and every network call krowk makes
without being asked. krowk has no telemetry and no crash reporting. -
cargo denyon every pull request and at every tag. A dependency
with a RustSec advisory, a licence outsidedeny.toml's list, or a
source other than crates.io fails CI and stops a release. -
krowk sync attachanswers approvals and steers the turn. Besides
prompts, a line typed on its stdin can be/approve REQUEST_ID,
/allow-session REQUEST_IDor/deny REQUEST_ID, answering the tool call
anapproval.requestedline names,/interrupt, which stops the running
turn, or/steer TEXT, which adds to it. Eachapproval.requestedline
carries itsrequestId, and stderr says which command answers it. Any
other line starting with/goes as a prompt, so skills and paths still
work, and//TEXTsends/TEXT.krowk help sync attachlists them. -
A synced session goes direct over Tailscale when it can.
krowk sync hostreads this machine's tailnet address, MagicDNS name and LAN address
from the localtailscaled, listens there, and names the addresses to
each viewer inside the sealed channel. A viewer tries them all beside the
relay and moves onto the first that passes the same ticket, challenge and
handshake the relay does, mid-session and without losing a frame; if the
direct path goes, or its welcome is more than three seconds late, it
falls back to the relay by itself and tries again later, less often each
time nothing is found (10 seconds, doubling to 5 minutes).krowk sync attachprints the path in use (sync.path:relay,
direct over Tailscaleordirect over LAN). Being on the tailnet lets
nobody in: direct paths are on only with the registry's ticket keys in
KROWK_RELAY_TICKET_KEYS, andKROWK_TAILSCALE_SAME_USER=1also turns
away a connection from another tailnet user. The LAN address is offered
only withKROWK_DIRECT_LAN=1, and never with the same-user check.
krowk hostslists the
tailnet's machines taggedtag:krowk-host. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
A session running on one machine can be watched and steered from
another. The host keeps the session's lease, renewing it every 20
seconds, streams it over the relay sealed end to end, and writes its log
to the registry as sealed chunks with a checkpoint to attach from.
Another of your machines attaches from the latest checkpoint in well
under half a second, follows the session live, and can prompt it, steer
or stop it, and answer its approvals. When the host is away the other
machine is read-only, and a prompt typed there waits and runs when the
host is back. A dropped connection resumes by itself with nothing lost.
Neither the relay nor the registry ever sees the session's content.
krowk sync host <session>syncs a session of this machine,krowk sync sessionslists what another machine can open, andkrowk sync attach <session>— orkrowk --resume <session>on a machine without the
session's log — follows it there. The relay isKROWK_RELAY_URL, or
krowk relay serveon this machine. -
Sync calls that act as this machine are signed by its own key.
Registering or approving a device, a lease call, writing a session or
its log, and asking for a relay ticket now carryX-Krowk-Device,
X-Krowk-TimestampandX-Krowk-Signature, an Ed25519 signature by
this machine's signing key. The registry refuses them unsigned, signed by
another key, more than five minutes off its clock, or sent twice, so a
workspace API key alone can no longer act as one of its devices. A
clock more than five minutes out makes these calls fail with
signature_stale. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
**Setting up ...
v0.12.0-rc4
Added
-
krowk sync initsets up your device list, with a 12-word recovery
kit. It asks you to sign in again in the browser, then makes the list's
first entry: this device and the recovery device the kit derives, with your
user key wrapped to both. The kit's words are shown once on stderr ([Enter]
when written down, [s] to skip) or written to--save FILE, 0600, and are
never typed back.--start-overmakes a new list; every other device on
the old one stops syncing and asks to be paired again. Run from a device
that holds your key, it keeps the old keys aside and seals the sessions it
can open again under the new list; running it again (under another
workspace's key, for that workspace's) goes through what is left, and
krowk sync recovery discard-olddrops the old keys when you say so. The 24-word recovery phrase is gone, and so is
krowk sync register: a device is registered by being on the list. -
krowk devices remove NAMEtakes a device off your list and rotates
your key away from it. It names every device the new key goes to before it
asks, needs a fresh sign-in, and removes any device revoked on the dashboard
in the same post, so no new key reaches one.krowk sync statusoffers to
finish a dashboard Revoke. Your other devices take the new key at their next
sync. -
krowk sync recovergets back in on a new machine from the kit's
words, typed at a prompt that doesn't echo them or piped in. It verifies
the list from its first entry, goes through every device on it with you to
keep or remove, and only then wraps your key, to what you kept. -
krowk sync recovery newreplaces the kit at once, with the old kit's
words, or from any device when you have none.krowk sync recovery checktests the words against the list, locally.krowk sync status
verifies the list against the head this device pinned, and says when there
is no kit; so does the TUI's status line. -
Pairing a device by a short code, in the library.
krowk_client::pairing
holds both sides ofkrowk devices addas sans-IO state machines: an
eight-character code (Crockford base32 less0and1, shownXXXX-XXXX,
typed in any case with spaces and dashes ignored), SPAKE2 in asymmetric mode
bound to the peer kind, the person and both device ids, and key confirmation
both ways before the new device's name is shown or anything is posted. One
failed step ends the pairing, and the new device's code is consumed by the
attempt, so a hostile registry gets one guess against each side. The SPAKE2 crate is held to magic-wormhole's vectors. Nothing
calls it yet. -
The client crypto for devices you own, not yet wired to any command:
a user key per person with generations, each wrapping the one before; a
12-word recovery kit that derives a recovery device; and a signed,
chained device list that every client verifies against the head it last
saw, refusing an older or forked list and any removal of the recovery
device. Only the current kit can replace the kit, or any device when there
is none. -
krowk devices addandkrowk sync joinpair a machine by a short
code.addshowsXXXX-XXXX, valid ten minutes and once;joinon the
new machine takes it at a prompt, never as an argument. Both machines check
the code, the paired one asksAdd '<name>' (<os>) to your devices? [Y/n],
and the new machine keeps the user key only once the chain it was sent
adds exactly its keys. A wrong code, or any failure on the new machine, a
dropped connection included, ends the pairing and asks for a new code. A
machine that was sent the key but never confirmed it is still listed, so
krowk devices removecan take it off; ^C onaddends the pairing. -
krowk sync hostseals under the device list as the registry has it
now, extended from this machine's pin, and refuses when the registry
cannot be asked, rather than sealing under a list a removal left behind. -
The stand-in registry holds devices you own.
krowk-devregistryserves
a person's signed device list (verified on every post by krowk-client's own
verifier), the user key wrapped to each device, keys bound to one device,
a fresh sign-in stamp, and the pairing mailbox: one live pairing per
person, ten minutes, ended by the first step out of turn. The device
approval endpoints answer410 sync_reset. -
Releases are signed, come with an SBOM, and are proven reproducible.
Each release carrieschecksums.txt.sigstore.json, a keyless Sigstore
signature over the checksums of every archive, bound to the release
workflow at that tag, and a CycloneDX SBOM of the full and lean builds,
each signed the same way. Before anything is published, the release builds
Linux x86-64 a second time on another runner from another path and stops
unless every binary matches byte for byte. The npm packages carry npm
provenance.SECURITY.mdsays how to verify a download and rebuild one. -
SECURITY.md: where to report a vulnerability (security@krowk.com),
what happens then, what is in scope, and every network call krowk makes
without being asked. krowk has no telemetry and no crash reporting. -
cargo denyon every pull request and at every tag. A dependency
with a RustSec advisory, a licence outsidedeny.toml's list, or a
source other than crates.io fails CI and stops a release. -
krowk sync attachanswers approvals and steers the turn. Besides
prompts, a line typed on its stdin can be/approve REQUEST_ID,
/allow-session REQUEST_IDor/deny REQUEST_ID, answering the tool call
anapproval.requestedline names,/interrupt, which stops the running
turn, or/steer TEXT, which adds to it. Eachapproval.requestedline
carries itsrequestId, and stderr says which command answers it. Any
other line starting with/goes as a prompt, so skills and paths still
work, and//TEXTsends/TEXT.krowk help sync attachlists them. -
A synced session goes direct over Tailscale when it can.
krowk sync hostreads this machine's tailnet address, MagicDNS name and LAN address
from the localtailscaled, listens there, and names the addresses to
each viewer inside the sealed channel. A viewer tries them all beside the
relay and moves onto the first that passes the same ticket, challenge and
handshake the relay does, mid-session and without losing a frame; if the
direct path goes, or its welcome is more than three seconds late, it
falls back to the relay by itself and tries again later, less often each
time nothing is found (10 seconds, doubling to 5 minutes).krowk sync attachprints the path in use (sync.path:relay,
direct over Tailscaleordirect over LAN). Being on the tailnet lets
nobody in: direct paths are on only with the registry's ticket keys in
KROWK_RELAY_TICKET_KEYS, andKROWK_TAILSCALE_SAME_USER=1also turns
away a connection from another tailnet user. The LAN address is offered
only withKROWK_DIRECT_LAN=1, and never with the same-user check.
krowk hostslists the
tailnet's machines taggedtag:krowk-host. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
A session running on one machine can be watched and steered from
another. The host keeps the session's lease, renewing it every 20
seconds, streams it over the relay sealed end to end, and writes its log
to the registry as sealed chunks with a checkpoint to attach from.
Another of your machines attaches from the latest checkpoint in well
under half a second, follows the session live, and can prompt it, steer
or stop it, and answer its approvals. When the host is away the other
machine is read-only, and a prompt typed there waits and runs when the
host is back. A dropped connection resumes by itself with nothing lost.
Neither the relay nor the registry ever sees the session's content.
krowk sync host <session>syncs a session of this machine,krowk sync sessionslists what another machine can open, andkrowk sync attach <session>— orkrowk --resume <session>on a machine without the
session's log — follows it there. The relay isKROWK_RELAY_URL, or
krowk relay serveon this machine. -
Sync calls that act as this machine are signed by its own key.
Registering or approving a device, a lease call, writing a session or
its log, and asking for a relay ticket now carryX-Krowk-Device,
X-Krowk-TimestampandX-Krowk-Signature, an Ed25519 signature by
this machine's signing key. The registry refuses them unsigned, signed by
another key, more than five minutes off its clock, or sent twice, so a
workspace API key alone can no longer act as one of its devices. A
clock more than five minutes out makes these calls fail with
signature_stale. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
**Setting up ...
v0.12.0-rc3
Added
-
krowk sync initsets up your device list, with a 12-word recovery
kit. It asks you to sign in again in the browser, then makes the list's
first entry: this device and the recovery device the kit derives, with your
user key wrapped to both. The kit's words are shown once on stderr ([Enter]
when written down, [s] to skip) or written to--save FILE, 0600, and are
never typed back.--start-overmakes a new list; every other device on
the old one stops syncing and asks to be paired again. Run from a device
that holds your key, it keeps the old keys aside and seals the sessions it
can open again under the new list; running it again (under another
workspace's key, for that workspace's) goes through what is left, and
krowk sync recovery discard-olddrops the old keys when you say so. The 24-word recovery phrase is gone, and so is
krowk sync register: a device is registered by being on the list. -
krowk devices remove NAMEtakes a device off your list and rotates
your key away from it. It names every device the new key goes to before it
asks, needs a fresh sign-in, and removes any device revoked on the dashboard
in the same post, so no new key reaches one.krowk sync statusoffers to
finish a dashboard Revoke. Your other devices take the new key at their next
sync. -
krowk sync recovergets back in on a new machine from the kit's
words, typed at a prompt that doesn't echo them or piped in. It verifies
the list from its first entry, goes through every device on it with you to
keep or remove, and only then wraps your key, to what you kept. -
krowk sync recovery newreplaces the kit at once, with the old kit's
words, or from any device when you have none.krowk sync recovery checktests the words against the list, locally.krowk sync status
verifies the list against the head this device pinned, and says when there
is no kit; so does the TUI's status line. -
Pairing a device by a short code, in the library.
krowk_client::pairing
holds both sides ofkrowk devices addas sans-IO state machines: an
eight-character code (Crockford base32 less0and1, shownXXXX-XXXX,
typed in any case with spaces and dashes ignored), SPAKE2 in asymmetric mode
bound to the peer kind, the person and both device ids, and key confirmation
both ways before the new device's name is shown or anything is posted. One
failed step ends the pairing, and the new device's code is consumed by the
attempt, so a hostile registry gets one guess against each side. The SPAKE2 crate is held to magic-wormhole's vectors. Nothing
calls it yet. -
The client crypto for devices you own, not yet wired to any command:
a user key per person with generations, each wrapping the one before; a
12-word recovery kit that derives a recovery device; and a signed,
chained device list that every client verifies against the head it last
saw, refusing an older or forked list and any removal of the recovery
device. Only the current kit can replace the kit, or any device when there
is none. -
krowk devices addandkrowk sync joinpair a machine by a short
code.addshowsXXXX-XXXX, valid ten minutes and once;joinon the
new machine takes it at a prompt, never as an argument. Both machines check
the code, the paired one asksAdd '<name>' (<os>) to your devices? [Y/n],
and the new machine keeps the user key only once the chain it was sent
adds exactly its keys. A wrong code, or any failure on the new machine, a
dropped connection included, ends the pairing and asks for a new code. A
machine that was sent the key but never confirmed it is still listed, so
krowk devices removecan take it off; ^C onaddends the pairing. -
krowk sync hostseals under the device list as the registry has it
now, extended from this machine's pin, and refuses when the registry
cannot be asked, rather than sealing under a list a removal left behind. -
The stand-in registry holds devices you own.
krowk-devregistryserves
a person's signed device list (verified on every post by krowk-client's own
verifier), the user key wrapped to each device, keys bound to one device,
a fresh sign-in stamp, and the pairing mailbox: one live pairing per
person, ten minutes, ended by the first step out of turn. The device
approval endpoints answer410 sync_reset. -
Releases are signed, come with an SBOM, and are proven reproducible.
Each release carrieschecksums.txt.sigstore.json, a keyless Sigstore
signature over the checksums of every archive, bound to the release
workflow at that tag, and a CycloneDX SBOM of the full and lean builds,
each signed the same way. Before anything is published, the release builds
Linux x86-64 a second time on another runner from another path and stops
unless every binary matches byte for byte. The npm packages carry npm
provenance.SECURITY.mdsays how to verify a download and rebuild one. -
SECURITY.md: where to report a vulnerability (security@krowk.com),
what happens then, what is in scope, and every network call krowk makes
without being asked. krowk has no telemetry and no crash reporting. -
cargo denyon every pull request and at every tag. A dependency
with a RustSec advisory, a licence outsidedeny.toml's list, or a
source other than crates.io fails CI and stops a release. -
krowk sync attachanswers approvals and steers the turn. Besides
prompts, a line typed on its stdin can be/approve REQUEST_ID,
/allow-session REQUEST_IDor/deny REQUEST_ID, answering the tool call
anapproval.requestedline names,/interrupt, which stops the running
turn, or/steer TEXT, which adds to it. Eachapproval.requestedline
carries itsrequestId, and stderr says which command answers it. Any
other line starting with/goes as a prompt, so skills and paths still
work, and//TEXTsends/TEXT.krowk help sync attachlists them. -
A synced session goes direct over Tailscale when it can.
krowk sync hostreads this machine's tailnet address, MagicDNS name and LAN address
from the localtailscaled, listens there, and names the addresses to
each viewer inside the sealed channel. A viewer tries them all beside the
relay and moves onto the first that passes the same ticket, challenge and
handshake the relay does, mid-session and without losing a frame; if the
direct path goes, or its welcome is more than three seconds late, it
falls back to the relay by itself and tries again later, less often each
time nothing is found (10 seconds, doubling to 5 minutes).krowk sync attachprints the path in use (sync.path:relay,
direct over Tailscaleordirect over LAN). Being on the tailnet lets
nobody in: direct paths are on only with the registry's ticket keys in
KROWK_RELAY_TICKET_KEYS, andKROWK_TAILSCALE_SAME_USER=1also turns
away a connection from another tailnet user. The LAN address is offered
only withKROWK_DIRECT_LAN=1, and never with the same-user check.
krowk hostslists the
tailnet's machines taggedtag:krowk-host. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
A session running on one machine can be watched and steered from
another. The host keeps the session's lease, renewing it every 20
seconds, streams it over the relay sealed end to end, and writes its log
to the registry as sealed chunks with a checkpoint to attach from.
Another of your machines attaches from the latest checkpoint in well
under half a second, follows the session live, and can prompt it, steer
or stop it, and answer its approvals. When the host is away the other
machine is read-only, and a prompt typed there waits and runs when the
host is back. A dropped connection resumes by itself with nothing lost.
Neither the relay nor the registry ever sees the session's content.
krowk sync host <session>syncs a session of this machine,krowk sync sessionslists what another machine can open, andkrowk sync attach <session>— orkrowk --resume <session>on a machine without the
session's log — follows it there. The relay isKROWK_RELAY_URL, or
krowk relay serveon this machine. -
Sync calls that act as this machine are signed by its own key.
Registering or approving a device, a lease call, writing a session or
its log, and asking for a relay ticket now carryX-Krowk-Device,
X-Krowk-TimestampandX-Krowk-Signature, an Ed25519 signature by
this machine's signing key. The registry refuses them unsigned, signed by
another key, more than five minutes off its clock, or sent twice, so a
workspace API key alone can no longer act as one of its devices. A
clock more than five minutes out makes these calls fail with
signature_stale. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
**Setting up ...
v0.12.0-rc2
Added
-
Releases are signed, come with an SBOM, and are proven reproducible.
Each release carrieschecksums.txt.sigstore.json, a keyless Sigstore
signature over the checksums of every archive, bound to the release
workflow at that tag, and a CycloneDX SBOM of the full and lean builds,
each signed the same way. Before anything is published, the release builds
Linux x86-64 a second time on another runner from another path and stops
unless every binary matches byte for byte. The npm packages carry npm
provenance.SECURITY.mdsays how to verify a download and rebuild one. -
SECURITY.md: where to report a vulnerability (security@krowk.com),
what happens then, what is in scope, and every network call krowk makes
without being asked. krowk has no telemetry and no crash reporting. -
cargo denyon every pull request and at every tag. A dependency
with a RustSec advisory, a licence outsidedeny.toml's list, or a
source other than crates.io fails CI and stops a release. -
krowk sync attachanswers approvals and steers the turn. Besides
prompts, a line typed on its stdin can be/approve REQUEST_ID,
/allow-session REQUEST_IDor/deny REQUEST_ID, answering the tool call
anapproval.requestedline names,/interrupt, which stops the running
turn, or/steer TEXT, which adds to it. Eachapproval.requestedline
carries itsrequestId, and stderr says which command answers it. Any
other line starting with/goes as a prompt, so skills and paths still
work, and//TEXTsends/TEXT.krowk help sync attachlists them. -
A synced session goes direct over Tailscale when it can.
krowk sync hostreads this machine's tailnet address, MagicDNS name and LAN address
from the localtailscaled, listens there, and names the addresses to
each viewer inside the sealed channel. A viewer tries them all beside the
relay and moves onto the first that passes the same ticket, challenge and
handshake the relay does, mid-session and without losing a frame; if the
direct path goes, or its welcome is more than three seconds late, it
falls back to the relay by itself and tries again later, less often each
time nothing is found (10 seconds, doubling to 5 minutes).krowk sync attachprints the path in use (sync.path:relay,
direct over Tailscaleordirect over LAN). Being on the tailnet lets
nobody in: direct paths are on only with the registry's ticket keys in
KROWK_RELAY_TICKET_KEYS, andKROWK_TAILSCALE_SAME_USER=1also turns
away a connection from another tailnet user. The LAN address is offered
only withKROWK_DIRECT_LAN=1, and never with the same-user check.
krowk hostslists the
tailnet's machines taggedtag:krowk-host. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
A session running on one machine can be watched and steered from
another. The host keeps the session's lease, renewing it every 20
seconds, streams it over the relay sealed end to end, and writes its log
to the registry as sealed chunks with a checkpoint to attach from.
Another of your machines attaches from the latest checkpoint in well
under half a second, follows the session live, and can prompt it, steer
or stop it, and answer its approvals. When the host is away the other
machine is read-only, and a prompt typed there waits and runs when the
host is back. A dropped connection resumes by itself with nothing lost.
Neither the relay nor the registry ever sees the session's content.
krowk sync host <session>syncs a session of this machine,krowk sync sessionslists what another machine can open, andkrowk sync attach <session>— orkrowk --resume <session>on a machine without the
session's log — follows it there. The relay isKROWK_RELAY_URL, or
krowk relay serveon this machine. -
Sync calls that act as this machine are signed by its own key.
Registering or approving a device, a lease call, writing a session or
its log, and asking for a relay ticket now carryX-Krowk-Device,
X-Krowk-TimestampandX-Krowk-Signature, an Ed25519 signature by
this machine's signing key. The registry refuses them unsigned, signed by
another key, more than five minutes off its clock, or sent twice, so a
workspace API key alone can no longer act as one of its devices. A
clock more than five minutes out makes these calls fail with
signature_stale. -
A file a session publishes now says what made it. Besides the session
(krowk.session), each artifactpublishpushes records the engine that
ran the turn (krowk.engine:krowk,claude-codeor
codex-app-server), the model (gen_ai.request.model) and its provider
(gen_ai.system), so its card on krowk.com can link back to the session
with akrowk --resumecommand. A subagent's file names the subagent's
model. Nothing from the session's log is sent; the session id, engine,
model and provider are, in the clear, even for a synced session. -
Setting up sync now registers this machine's relay signing key.
krowk sync init,recoverandregister, andkrowk devices approve,
send the public half of the signing key beside the device key, andkrowk sync joinsends it with the approval request, so the approval registers
it. krowk's hosted relay uses it to tell this machine's connections from
anyone else's. The key is set once: a different one for the same device
is refused (signing_key_mismatch). -
The code
krowk sync joinshows to approve a machine changed. It
now covers both of the new machine's keys, so a request that copied its
device key with someone else's signing key shows a different code, and
krowk devices approveapproves only the one request whose code is
exactly the one you typed. Only one approval request per machine may
wait at a time. -
Relays admit devices on tickets the registry signs. A device joins
a relay with a short-lived ticket, which comes with the session's lease
for the host and on request for a viewer.krowk relay servenow takes
--ticket-keys FILE, the registry's ticket-signing public keys, instead
of--roster. The ticket travels in theX-Krowk-Ticketheader of the
WebSocket upgrade, and a connection without a good one is turned away
before it takes any room on the channel, so a flood of connections
cannot keep a session's devices off it. -
krowk relay serve --state DIRkeeps each channel's fence across
restarts. It is required when--addris reachable from the network,
like--origin, so a restart never lets a machine that lost the lease
host again. On loopback the relay may keep it in memory. -
A relay keeps development sessions apart from production ones.
A relay join now says which it is,env"production" or "development",
andkrowk relay servegives each its own channel of a session, so a
developer's session never shares a relay buffer or a viewer with a
user's. krowk says "development" for a debug build, for
KROWK_ENV=development, or for any registry butapi.krowk.com. -
krowk relay serveruns a relay of your own. It carries a synced
session between the machine running it and the devices watching it, and
only ever sealed bytes: it refuses anything not encrypted, and never
stores it. A device joins by signing the relay's challenge with a new
per-device signing key (signing.jsonin krowk's home, made the first
time it is needed); only the machine holding the session's lease may
host it. A device that reconnects picks up where it left off from the
relay's short buffer, and the relay answers heartbeats itself, so a busy
or quiet host never looks gone. It listens on 127.0.0.1:7790 unless
--addrsays otherwise; an address reachable from the network also
needs--origin, the URL devices dial it by, and the banner says it is
open. Nothing connects to it on its own yet; the host daemon and the
terminal will once syncing sessions lands.krowk help --alllists it. -
A synced session's log can now be stored, encrypted, in the registry.
The machine holding a session's lease seals each piece of the log on its
own side and uploads it straight to storage. Each piece is numbered,
names the lease it was written under, and is chained to the piece before
it, and the last one is marked. A device reading the log back refuses a
piece that is repeated, out of order, missing, or spliced in from another
machine's copy of the log, and can tell a finished log. A piece is
refused unless it comes with the lease's current token, and one a
previous lease holder left half-uploaded is replaced rather than blocking
the log. Pieces are at most 64 MiB. The registry and storage only ever hold the sealed bytes.
The pieces count on your workspace's storage meter, and never show up in
krowk uploads list, on a card, or anywhere else a push's artifacts do.
Nothing writes them yet on its own; the host daemon will once syncing
sessions lands. -
A device the workspace's owner revoked by resetting sync can come back.
Runkrowk sync registeron it after the reset. The fix line on a
device_revokedrefusal now says so, where it used to point at
krowk sync recover, which could not help. -
Add a machine to sync by approving it from one that already syncs,
with no recovery phrase. On the new machine,krowk sync joinshows a...