Skip to content

Releases: krowkcom/krowk

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 12 Oct 16:07
cabdd28

Added

  • An agent definition can name the MCP servers its subagent may use.
    In its tools: list, mcp__github gives the subagent the whole
    github server and mcp__linear__create_issue that one tool, as in
    Claude Code; Mcp(github) and Mcp(linear:create_issue) work too, and
    mcp__* gives it every server. A name krowk can't read, such as the
    pattern mcp__github__get_*, is left out with a notice. Such
    a subagent gets mcp_search and mcp_call for 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 a tools: line got no MCP at all; one
    without a tools: 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 /connect clears what it reported, and krowk reports it
    again once it has the terminal back. A terminal that doesn't know the
    sequence ignores it.
  • /connect tells 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 -p leaves a record of how it ended on your terminal. A
    terminal that reads OSC 7501, the Program Status Protocol (Ghostty, Rex),
    now shows a krowk -p you 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
    (bash with run_in_background) and keep working: a dev server, a long
    test run, a build. It reads the output later with bash_output and stops
    the command with kill_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 (subagent with run_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 a subagent.status line 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 with krowk 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, and x on
    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 say running 0.0s.
  • **A running subagent's line says whether it is busy, ...
Read more

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 16:19
c1d2b87

Added

  • A subagent can work in a git worktree of its own. Give the
    subagent tool isolation: "worktree", or put isolation: worktree in
    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 branch krowk/<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 your HEAD, and
    the branch starts there; with nothing uncommitted it starts at HEAD.
    Your index, HEAD and 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
    with isolation: 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 with isolation: "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 and HEAD are not
    touched. The worktree and its branch are then removed, its final state
    kept as refs/krowk/snapshots/<hex> for 30 days, and the summary the
    parent gets ends with Changes 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 with Changes 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,
    .codex or .krowk directory, which the file tools only change with
    your say: its summary ends with Changes 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 --worktree session's work
    home. It refuses (exit 4) while that session is still running, or when
    a file conflicts, and refuses (exit 1) a --to that 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; its origin stays 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 .gitmodules that
    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's target
    and node_modules into a new worktree before the subagent starts, so its
    first cargo build finds nothing to compile and the clone takes almost
    no disk. target is skipped while a build is running in it, and
    node_modules unless the worktree's lockfile (package-lock.json,
    pnpm-lock.yaml, yarn.lock, bun.lock or bun.lockb) is the same as
    your checkout's. Anywhere else, and on Windows, nothing is copied. Choose
    the directories with worktrees.seed in config.json, for example
    {"worktrees": {"seed": ["target", ".venv"]}}; [] turns it off.
    What was skipped and why is in seed.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
    the btrfs program is installed, a new worktree for a subagent or a
    krowk --worktree session 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 build compiles 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 .worktreeinclude at the
    top of your main checkout, in .gitignore syntax (.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.seed directories are left to seeding. A file that can't be
    copied is skipped and noted in seed.log. Without a
    .worktreeinclude, nothing is copied.

  • A subagent's worktree can run your project's setup command first.
    Set worktrees.setup to 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.json for 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 gets KROWK_PROJECT_ROOT (your main checkout),
    KROWK_WORKTREE_PATH and KROWK_PORT_BASE: each live worktree holds
    its own ten ports from 20000 up (20000, 20010, …), and the subagent's
    own commands see KROWK_PORT_BASE too, 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 in krowk-setup.log in the worktree's git
    directory. When it fails, or runs past worktrees.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 worktrees lists, removes and prunes the worktrees krowk
    kept.
    krowk worktrees shows 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 (--json for 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, as refs/krowk/snapshots/<hex>, and a HEAD its
    branch doesn't hold (the agent detached it, or switched branch, and
    committed) as refs/krowk/snapshots/<hex>-head, and prints the commands
    that bring the worktree back. Ignored files, such as build output and
    the copies .worktreeinclude made, don't count as changes and are
    deleted with it. krowk worktrees prune clears 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 --worktree and krowk -p --worktree start 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...

Read more

v0.12.1

Choose a tag to compare

@github-actions github-actions released this 04 Oct 10:52
ba6c717

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. sync and devices move up to the AGENT heading in krowk 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 with krowk, krowk login
    and krowk sync init, then krowk 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 krowk

checksums.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.txt

The .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

Choose a tag to compare

@github-actions github-actions released this 04 Oct 10:22
e8f89dc

Added

  • krowk sync attach opens 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, /new and /sessions say 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 init sets 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-over makes 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-old drops 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 NAME takes 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 status offers to
    finish a dashboard Revoke. Your other devices take the new key at their next
    sync.

  • krowk sync recover gets 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 new replaces the kit at once, with the old kit's
    words, or from any device when you have none. krowk sync recovery check tests 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 of krowk devices add as sans-IO state machines: an
    eight-character code (Crockford base32 less 0 and 1, shown XXXX-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 add and krowk sync join pair a machine by a short
    code.
    add shows XXXX-XXXX, valid ten minutes and once; join on the
    new machine takes it at a prompt, never as an argument. Both machines check
    the code, the paired one asks Add '<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 remove can take it off; ^C on add ends the pairing.

  • krowk sync host seals 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-devregistry serves
    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 answer 410 sync_reset.

  • Releases are signed, come with an SBOM, and are proven reproducible.
    Each release carries checksums.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.md says 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 deny on every pull request and at every tag. A dependency
    with a RustSec advisory, a licence outside deny.toml's list, or a
    source other than crates.io fails CI and stops a release.

  • krowk sync attach answers approvals and steers the turn. Besides
    prompts, a line typed on its stdin can be /approve REQUEST_ID,
    /allow-session REQUEST_ID or /deny REQUEST_ID, answering the tool call
    an approval.requested line names, /interrupt, which stops the running
    turn, or /steer TEXT, which adds to it. Each approval.requested line
    carries its requestId, and stderr says which command answers it. Any
    other line starting with / goes as a prompt, so skills and paths still
    work, and //TEXT sends /TEXT. krowk help sync attach lists them.

  • A synced session goes direct over Tailscale when it can. krowk sync host reads this machine's tailnet address, MagicDNS name and LAN address
    from the local tailscaled, 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 attach prints the path in use (sync.path: relay,
    direct over Tailscale or direct 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, and KROWK_TAILSCALE_SAME_USER=1 also turns
    away a connection from another tailnet user. The LAN address is offered
    only with KROWK_DIRECT_LAN=1, and never with the same-user check.
    krowk hosts lists the
    tailnet's machines tagged tag:krowk-host.

  • A file a session publishes now says what made it. Besides the session
    (krowk.session), each artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 sessions lists what another machine can open, and krowk sync attach <session> — or krowk --resume <session> on a machine without the
    session's log — follows it there. The relay is KROWK_RELAY_URL, or
    krowk relay serve on 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 carry X-Krowk-Device,
    X-Krowk-Timestamp and X-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...

Read more

v0.12.0-rc7

v0.12.0-rc7 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 04 Oct 07:34
f80a57e

Added

  • krowk sync attach opens 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, /new and /sessions say 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 init sets 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-over makes 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-old drops 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 NAME takes 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 status offers to
    finish a dashboard Revoke. Your other devices take the new key at their next
    sync.

  • krowk sync recover gets 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 new replaces the kit at once, with the old kit's
    words, or from any device when you have none. krowk sync recovery check tests 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 of krowk devices add as sans-IO state machines: an
    eight-character code (Crockford base32 less 0 and 1, shown XXXX-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 add and krowk sync join pair a machine by a short
    code.
    add shows XXXX-XXXX, valid ten minutes and once; join on the
    new machine takes it at a prompt, never as an argument. Both machines check
    the code, the paired one asks Add '<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 remove can take it off; ^C on add ends the pairing.

  • krowk sync host seals 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-devregistry serves
    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 answer 410 sync_reset.

  • Releases are signed, come with an SBOM, and are proven reproducible.
    Each release carries checksums.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.md says 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 deny on every pull request and at every tag. A dependency
    with a RustSec advisory, a licence outside deny.toml's list, or a
    source other than crates.io fails CI and stops a release.

  • krowk sync attach answers approvals and steers the turn. Besides
    prompts, a line typed on its stdin can be /approve REQUEST_ID,
    /allow-session REQUEST_ID or /deny REQUEST_ID, answering the tool call
    an approval.requested line names, /interrupt, which stops the running
    turn, or /steer TEXT, which adds to it. Each approval.requested line
    carries its requestId, and stderr says which command answers it. Any
    other line starting with / goes as a prompt, so skills and paths still
    work, and //TEXT sends /TEXT. krowk help sync attach lists them.

  • A synced session goes direct over Tailscale when it can. krowk sync host reads this machine's tailnet address, MagicDNS name and LAN address
    from the local tailscaled, 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 attach prints the path in use (sync.path: relay,
    direct over Tailscale or direct 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, and KROWK_TAILSCALE_SAME_USER=1 also turns
    away a connection from another tailnet user. The LAN address is offered
    only with KROWK_DIRECT_LAN=1, and never with the same-user check.
    krowk hosts lists the
    tailnet's machines tagged tag:krowk-host.

  • A file a session publishes now says what made it. Besides the session
    (krowk.session), each artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 sessions lists what another machine can open, and krowk sync attach <session> — or krowk --resume <session> on a machine without the
    session's log — follows it there. The relay is KROWK_RELAY_URL, or
    krowk relay serve on 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 carry X-Krowk-Device,
    X-Krowk-Timestamp and X-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...

Read more

v0.12.0-rc6

v0.12.0-rc6 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Oct 14:51
2edbf8b

Added

  • krowk sync init sets 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-over makes 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-old drops 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 NAME takes 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 status offers to
    finish a dashboard Revoke. Your other devices take the new key at their next
    sync.

  • krowk sync recover gets 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 new replaces the kit at once, with the old kit's
    words, or from any device when you have none. krowk sync recovery check tests 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 of krowk devices add as sans-IO state machines: an
    eight-character code (Crockford base32 less 0 and 1, shown XXXX-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 add and krowk sync join pair a machine by a short
    code.
    add shows XXXX-XXXX, valid ten minutes and once; join on the
    new machine takes it at a prompt, never as an argument. Both machines check
    the code, the paired one asks Add '<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 remove can take it off; ^C on add ends the pairing.

  • krowk sync host seals 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-devregistry serves
    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 answer 410 sync_reset.

  • Releases are signed, come with an SBOM, and are proven reproducible.
    Each release carries checksums.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.md says 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 deny on every pull request and at every tag. A dependency
    with a RustSec advisory, a licence outside deny.toml's list, or a
    source other than crates.io fails CI and stops a release.

  • krowk sync attach answers approvals and steers the turn. Besides
    prompts, a line typed on its stdin can be /approve REQUEST_ID,
    /allow-session REQUEST_ID or /deny REQUEST_ID, answering the tool call
    an approval.requested line names, /interrupt, which stops the running
    turn, or /steer TEXT, which adds to it. Each approval.requested line
    carries its requestId, and stderr says which command answers it. Any
    other line starting with / goes as a prompt, so skills and paths still
    work, and //TEXT sends /TEXT. krowk help sync attach lists them.

  • A synced session goes direct over Tailscale when it can. krowk sync host reads this machine's tailnet address, MagicDNS name and LAN address
    from the local tailscaled, 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 attach prints the path in use (sync.path: relay,
    direct over Tailscale or direct 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, and KROWK_TAILSCALE_SAME_USER=1 also turns
    away a connection from another tailnet user. The LAN address is offered
    only with KROWK_DIRECT_LAN=1, and never with the same-user check.
    krowk hosts lists the
    tailnet's machines tagged tag:krowk-host.

  • A file a session publishes now says what made it. Besides the session
    (krowk.session), each artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 sessions lists what another machine can open, and krowk sync attach <session> — or krowk --resume <session> on a machine without the
    session's log — follows it there. The relay is KROWK_RELAY_URL, or
    krowk relay serve on 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 carry X-Krowk-Device,
    X-Krowk-Timestamp and X-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 artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 ...

Read more

v0.12.0-rc5

v0.12.0-rc5 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Oct 12:55
376c101

Added

  • krowk sync init sets 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-over makes 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-old drops 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 NAME takes 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 status offers to
    finish a dashboard Revoke. Your other devices take the new key at their next
    sync.

  • krowk sync recover gets 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 new replaces the kit at once, with the old kit's
    words, or from any device when you have none. krowk sync recovery check tests 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 of krowk devices add as sans-IO state machines: an
    eight-character code (Crockford base32 less 0 and 1, shown XXXX-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 add and krowk sync join pair a machine by a short
    code.
    add shows XXXX-XXXX, valid ten minutes and once; join on the
    new machine takes it at a prompt, never as an argument. Both machines check
    the code, the paired one asks Add '<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 remove can take it off; ^C on add ends the pairing.

  • krowk sync host seals 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-devregistry serves
    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 answer 410 sync_reset.

  • Releases are signed, come with an SBOM, and are proven reproducible.
    Each release carries checksums.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.md says 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 deny on every pull request and at every tag. A dependency
    with a RustSec advisory, a licence outside deny.toml's list, or a
    source other than crates.io fails CI and stops a release.

  • krowk sync attach answers approvals and steers the turn. Besides
    prompts, a line typed on its stdin can be /approve REQUEST_ID,
    /allow-session REQUEST_ID or /deny REQUEST_ID, answering the tool call
    an approval.requested line names, /interrupt, which stops the running
    turn, or /steer TEXT, which adds to it. Each approval.requested line
    carries its requestId, and stderr says which command answers it. Any
    other line starting with / goes as a prompt, so skills and paths still
    work, and //TEXT sends /TEXT. krowk help sync attach lists them.

  • A synced session goes direct over Tailscale when it can. krowk sync host reads this machine's tailnet address, MagicDNS name and LAN address
    from the local tailscaled, 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 attach prints the path in use (sync.path: relay,
    direct over Tailscale or direct 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, and KROWK_TAILSCALE_SAME_USER=1 also turns
    away a connection from another tailnet user. The LAN address is offered
    only with KROWK_DIRECT_LAN=1, and never with the same-user check.
    krowk hosts lists the
    tailnet's machines tagged tag:krowk-host.

  • A file a session publishes now says what made it. Besides the session
    (krowk.session), each artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 sessions lists what another machine can open, and krowk sync attach <session> — or krowk --resume <session> on a machine without the
    session's log — follows it there. The relay is KROWK_RELAY_URL, or
    krowk relay serve on 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 carry X-Krowk-Device,
    X-Krowk-Timestamp and X-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 artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 ...

Read more

v0.12.0-rc4

v0.12.0-rc4 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Oct 10:28
7b06984

Added

  • krowk sync init sets 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-over makes 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-old drops 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 NAME takes 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 status offers to
    finish a dashboard Revoke. Your other devices take the new key at their next
    sync.

  • krowk sync recover gets 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 new replaces the kit at once, with the old kit's
    words, or from any device when you have none. krowk sync recovery check tests 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 of krowk devices add as sans-IO state machines: an
    eight-character code (Crockford base32 less 0 and 1, shown XXXX-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 add and krowk sync join pair a machine by a short
    code.
    add shows XXXX-XXXX, valid ten minutes and once; join on the
    new machine takes it at a prompt, never as an argument. Both machines check
    the code, the paired one asks Add '<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 remove can take it off; ^C on add ends the pairing.

  • krowk sync host seals 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-devregistry serves
    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 answer 410 sync_reset.

  • Releases are signed, come with an SBOM, and are proven reproducible.
    Each release carries checksums.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.md says 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 deny on every pull request and at every tag. A dependency
    with a RustSec advisory, a licence outside deny.toml's list, or a
    source other than crates.io fails CI and stops a release.

  • krowk sync attach answers approvals and steers the turn. Besides
    prompts, a line typed on its stdin can be /approve REQUEST_ID,
    /allow-session REQUEST_ID or /deny REQUEST_ID, answering the tool call
    an approval.requested line names, /interrupt, which stops the running
    turn, or /steer TEXT, which adds to it. Each approval.requested line
    carries its requestId, and stderr says which command answers it. Any
    other line starting with / goes as a prompt, so skills and paths still
    work, and //TEXT sends /TEXT. krowk help sync attach lists them.

  • A synced session goes direct over Tailscale when it can. krowk sync host reads this machine's tailnet address, MagicDNS name and LAN address
    from the local tailscaled, 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 attach prints the path in use (sync.path: relay,
    direct over Tailscale or direct 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, and KROWK_TAILSCALE_SAME_USER=1 also turns
    away a connection from another tailnet user. The LAN address is offered
    only with KROWK_DIRECT_LAN=1, and never with the same-user check.
    krowk hosts lists the
    tailnet's machines tagged tag:krowk-host.

  • A file a session publishes now says what made it. Besides the session
    (krowk.session), each artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 sessions lists what another machine can open, and krowk sync attach <session> — or krowk --resume <session> on a machine without the
    session's log — follows it there. The relay is KROWK_RELAY_URL, or
    krowk relay serve on 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 carry X-Krowk-Device,
    X-Krowk-Timestamp and X-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 artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 ...

Read more

v0.12.0-rc3

v0.12.0-rc3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Oct 08:01
cff3962

Added

  • krowk sync init sets 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-over makes 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-old drops 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 NAME takes 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 status offers to
    finish a dashboard Revoke. Your other devices take the new key at their next
    sync.

  • krowk sync recover gets 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 new replaces the kit at once, with the old kit's
    words, or from any device when you have none. krowk sync recovery check tests 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 of krowk devices add as sans-IO state machines: an
    eight-character code (Crockford base32 less 0 and 1, shown XXXX-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 add and krowk sync join pair a machine by a short
    code.
    add shows XXXX-XXXX, valid ten minutes and once; join on the
    new machine takes it at a prompt, never as an argument. Both machines check
    the code, the paired one asks Add '<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 remove can take it off; ^C on add ends the pairing.

  • krowk sync host seals 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-devregistry serves
    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 answer 410 sync_reset.

  • Releases are signed, come with an SBOM, and are proven reproducible.
    Each release carries checksums.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.md says 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 deny on every pull request and at every tag. A dependency
    with a RustSec advisory, a licence outside deny.toml's list, or a
    source other than crates.io fails CI and stops a release.

  • krowk sync attach answers approvals and steers the turn. Besides
    prompts, a line typed on its stdin can be /approve REQUEST_ID,
    /allow-session REQUEST_ID or /deny REQUEST_ID, answering the tool call
    an approval.requested line names, /interrupt, which stops the running
    turn, or /steer TEXT, which adds to it. Each approval.requested line
    carries its requestId, and stderr says which command answers it. Any
    other line starting with / goes as a prompt, so skills and paths still
    work, and //TEXT sends /TEXT. krowk help sync attach lists them.

  • A synced session goes direct over Tailscale when it can. krowk sync host reads this machine's tailnet address, MagicDNS name and LAN address
    from the local tailscaled, 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 attach prints the path in use (sync.path: relay,
    direct over Tailscale or direct 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, and KROWK_TAILSCALE_SAME_USER=1 also turns
    away a connection from another tailnet user. The LAN address is offered
    only with KROWK_DIRECT_LAN=1, and never with the same-user check.
    krowk hosts lists the
    tailnet's machines tagged tag:krowk-host.

  • A file a session publishes now says what made it. Besides the session
    (krowk.session), each artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 sessions lists what another machine can open, and krowk sync attach <session> — or krowk --resume <session> on a machine without the
    session's log — follows it there. The relay is KROWK_RELAY_URL, or
    krowk relay serve on 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 carry X-Krowk-Device,
    X-Krowk-Timestamp and X-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 artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 ...

Read more

v0.12.0-rc2

v0.12.0-rc2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 30 Sep 06:29
32243e9

Added

  • Releases are signed, come with an SBOM, and are proven reproducible.
    Each release carries checksums.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.md says 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 deny on every pull request and at every tag. A dependency
    with a RustSec advisory, a licence outside deny.toml's list, or a
    source other than crates.io fails CI and stops a release.

  • krowk sync attach answers approvals and steers the turn. Besides
    prompts, a line typed on its stdin can be /approve REQUEST_ID,
    /allow-session REQUEST_ID or /deny REQUEST_ID, answering the tool call
    an approval.requested line names, /interrupt, which stops the running
    turn, or /steer TEXT, which adds to it. Each approval.requested line
    carries its requestId, and stderr says which command answers it. Any
    other line starting with / goes as a prompt, so skills and paths still
    work, and //TEXT sends /TEXT. krowk help sync attach lists them.

  • A synced session goes direct over Tailscale when it can. krowk sync host reads this machine's tailnet address, MagicDNS name and LAN address
    from the local tailscaled, 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 attach prints the path in use (sync.path: relay,
    direct over Tailscale or direct 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, and KROWK_TAILSCALE_SAME_USER=1 also turns
    away a connection from another tailnet user. The LAN address is offered
    only with KROWK_DIRECT_LAN=1, and never with the same-user check.
    krowk hosts lists the
    tailnet's machines tagged tag:krowk-host.

  • A file a session publishes now says what made it. Besides the session
    (krowk.session), each artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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 sessions lists what another machine can open, and krowk sync attach <session> — or krowk --resume <session> on a machine without the
    session's log — follows it there. The relay is KROWK_RELAY_URL, or
    krowk relay serve on 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 carry X-Krowk-Device,
    X-Krowk-Timestamp and X-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 artifact publish pushes records the engine that
    ran the turn (krowk.engine: krowk, claude-code or
    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 a krowk --resume command. 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, recover and register, and krowk devices approve,
    send the public half of the signing key beside the device key, and krowk sync join sends 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 join shows 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 approve approves 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 serve now takes
    --ticket-keys FILE, the registry's ticket-signing public keys, instead
    of --roster. The ticket travels in the X-Krowk-Ticket header 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 DIR keeps each channel's fence across
    restarts.
    It is required when --addr is 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",
    and krowk relay serve gives 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 but api.krowk.com.

  • krowk relay serve runs 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.json in 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
    --addr says 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 --all lists 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.
    Run krowk sync register on it after the reset. The fix line on a
    device_revoked refusal 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 join shows a...

Read more