Knowl Cloud

A hosted workspace shares knowledge across a team. It is entirely opt-in: a repository that never runs knowl cloud connect behaves exactly as it did before, and nothing here costs it anything.

Agents read a local replica, never the server. Connecting syncs the team's published knowledge into a machine-local database under ~/.knowl/cloud/<workspace>/, and queries search that copy alongside your own. No search request leaves the machine, so retrieval stays as fast offline as on, and your query text is never sent anywhere. The web console is what queries the server live.

The replica is a replica: deleting it is always safe, and the next pull rebuilds it from scratch.

Command Purpose
knowl cloud login [--api <host>] Sign in once, by device code. The credential lives in ~/.knowl, never in .knowl/config.json
knowl cloud logout [--api <host>] Clear the stored credential
knowl cloud workspaces [--api <host>] List the workspaces this machine can reach, before connecting to one
knowl cloud connect [--workspace <id>] [--remote <name>] [--repo <name>] Point this project at a workspace. Publishes nothing
knowl cloud pull Fetch team knowledge into the local replica
knowl cloud stage [--id <ids...>] [--category <list>] [--apply] Queue knowledge for the team. Bare opens the same picker; naming flags dry-runs until --apply
knowl cloud push Send staged knowledge. Works from any branch
knowl cloud retract <id> --reason <text> Remove a published atom for good. Works from any branch
knowl cloud send [--id <ids...>] [--query <text>] [--expires-in <hours>] [--words <count>] Seal a few atoms for one person and print a code. Expires; collected once
knowl cloud send --list / --revoke <code-or-id> What you have in flight and whether it was collected; destroy one early
knowl cloud receive <code> Collect atoms somebody sent you. Shows who and how many before taking it
knowl cloud status What is connected, how stale the replica is, and what is staged

Pointing at a different server

Every command takes --api <host>, and knowl cloud connect records the host in the repository's config, so pull, push and status remember it afterwards. knowl cloud login is per-machine rather than per-repository and remembers nothing, so for a self-hosted or tunnelled server set it once:

export KNOWL_API_HOST=https://knowl.example.internal

--api still wins where it is given. Unset, the default is the hosted service.

Identity and connection

A project publishes under one identity, resolved by the first of these that applies:

  1. --repo <name>, if you give one.
  2. Its normalized git remote, when it has one. This is the best answer available, because it is identical for everyone who clones — two people who cloned to different paths publish to the same place without anyone typing anything. In a fork, origin is yours and upstream is the team's; --remote picks, and the choice is recorded in config. A project below the git root is qualified with #subpath, so a monorepo's packages do not pool their knowledge.
  3. Its directory name.

Git is not required. A folder of notes is a project, and so is work that has never been pushed anywhere; neither needs a remote, a .git, or git on PATH. The identity is a label the server groups by, not a claim about version control.

A directory name is not unique across machines, so two people who each keep notes in ~/notes and connect to one workspace would share a bucket. knowl cloud connect says when it fell back to the directory, and --repo <name> settles it.

Because the identity is derived, it can move on its own — add a remote to a project that had none and the answer changes. connect refuses that rather than re-keying silently, because anything already pushed stays filed under the old name and the server rejects writes to it from a different one. Re-run with --repo <old-name> to keep publishing as before.

Naming a remote that does not exist is still an error: --remote upstream on a repo without an upstream is a typo, and answering it with the directory name would file the knowledge somewhere nobody is looking.

The pointer written into .knowl/config.json holds the API host, workspace and repo identity and never a credential. That file is deliberately committable, so a teammate clones, runs knowl cloud login, and is in. Someone who clones without membership still gets a fully working local Knowl; the team half simply reports itself unavailable.

Knowledge stages itself; sending stays yours

Once a repository is connected, knowledge written from then on is queued for the team as it is written. You do not have to remember to stage it. Nothing is sent by that: staging records an intent, and a separate push is what puts it in front of anyone.

knowl cloud status                # what is queued
knowl cloud push                  # send it — from any branch

Three ways to say "not this one", in the order you will want them:

knowl store "..." --local         # at write time: never publish this atom
knowl cloud unstage <id>          # after the fact: take it back out of the queue
knowl cloud unstage <id> --forever   # and never queue it again automatically

--local is the one to reach for. An atom that is only true of this machine — an absolute path, an environment quirk — has to say so when it is written, because that is the only moment anyone knows. An agent says the same thing with local: true on knowl_store.

Turn the whole thing off for a repository with knowl config set cloud.autoStage false, or at connect time with knowl cloud connect --no-auto-stage.

Backfilling an existing store is a separate act. Connecting queues nothing that already existed, so a store that predates the connection is caught up deliberately:

knowl cloud stage                                # pick categories from a list
knowl cloud stage --category decision --apply    # or name them

A bare call opens a picker with the categories worth sharing already ticked and a count beside each; confirming it is the apply. Naming categories skips the picker and dry-runs until --apply.

Publishing works from any branch; reporting drift does not

Staging and sending are both ungated:

knowl cloud push                  # from any branch, at any time

Publishing adds an atom, and the worst case is knowledge that arrives early — which an update, a supersede or knowl cloud retract all answer. This used to refuse from anything but an up-to-date default branch. The gate could not tell knowledge about code from knowledge about anything else, so a pricing decision or a piece of market research waited on a merge it had nothing to do with, and auto-push skipped without saying why.

Reporting drift upward is still gated, and the line is what the act does rather than which branch you are on. A drift report retires a colleague's atom for the whole workspace, and from a checkout behind the default branch, code that was merged and code that was deleted look identical. Adding from a bad vantage is recoverable; retiring from one is not.

knowl cloud push shows what it is about to send and asks, because sending cannot be undone except by knowl cloud retract, which is a hard delete. --yes skips the question; without a terminal it is required, so silence is never read as consent.

Automatic sending is off, and turning it on is per machine. knowl cloud autopush on records standing consent for this workspace on this machine only — it is not written to .knowl/config.json and no teammate or CI inherits it. It still sends only what it showed itself: a queue that changed underneath it is refused, not sent.

A big queue goes in small requests, and a slow server makes them smaller rather than fatal. Publishing embeds each atom on the server, so a large batch can outrun the request budget. The push sends 20 at a time, and on a timeout it halves the batch and tries again rather than failing the whole backlog. Anything already accepted is recorded before the failure, so running the push again resumes where it stopped instead of starting over.

A staged atom you then replace locally can no longer be sent, and both commands now say so. knowl cloud stage reports how many named ids were replaced by a newer write, and knowl cloud status splits the queue into what a push can still move and what it cannot:

Staged:    118 staged (118 new, 0 correction(s)) on main, not yet sent.
           109 of those can still be sent; 9 were replaced by a newer write after being staged.

Without that split the count never reaches zero by pushing, and no command explains why. Stage the atom that replaced them instead.

Handing knowledge to a person

push is everyone on this team, permanently. send is you, specifically, right now — a handful of atoms, sealed, collected once, and expiring whether or not anybody takes them.

knowl cloud send --query "retry policy"
  3 atom(s) sealed. Hand this to them:
      knowl cloud receive owl-cascade-ridge-plum-tin

knowl cloud receive owl-cascade-ridge-plum-tin
  From: platform · 3 atom(s) · expires 2026-08-14T09:00:00Z
  ◆  Collect it? This can only be done once.
  │  ● Decline (the code still works until it expires)
  │  ○ Accept (import the atoms and spend the code)

Both ends ask before the irreversible half — receive always, send when --query chose the atoms rather than --id. Decline is preselected, so a bare Enter costs nothing. --yes skips the menu. Without a terminal to ask, neither command guesses: it prints what it would have done and exits non-zero, so a script that only meant to look at a bundle cannot spend it.

A send carries the atoms you chose and nothing else — not your learned skills, and not your forget-log. A backup means everything, which is what knowl export is for; handing a few atoms to one person means those atoms. The recipient may share no workspace with you, so the bundle carries no record of knowledge you deleted and no copy of a skill you never offered.

The server cannot read what you send. Your machine mints a five-word code, derives an encryption key and a mailbox id from it under separate labels, and uploads only the id and the sealed bytes. The code travels between two humans over whatever channel they already use, and it is printed once — it is not stored, not logged, and cannot be recovered if lost.

Guessing the code is made expensive, not just improbable. Five words is about 2⁵⁵, which a GPU rig could once have ground through against a stolen database inside a bundle's own 24–72 hour life — because the derivation was fast. Both halves now come off Argon2id at 64 MiB, so each guess costs about a second and a rig's worth of memory rather than a hash. That is why send and receive pause for a moment before they do anything.

--words 6 mints a six-word code instead, about 2⁶⁶. Worth having alongside the slow derivation and no substitute for it: what made 2⁵⁵ reachable was the cost per guess.

Codes minted by knowl 5.1.0 still work. A receiver looks for the new mailbox first and the old one second, so an in-flight bundle from an un-upgraded sender is collected exactly as before.

Both ends need a Knowl Cloud account; neither needs the same workspace. That is the whole capability push does not have. Requiring an account is what makes guessing a code rate-limited and attributable, so possession of the code is never the only thing standing between a stranger and a bundle.

What arrives is marked as imported. Received atoms carry an origin no repo name can equal, so they can never be promoted or published from your repo as your own work. That is the same machinery knowl import uses, and it means a handoff cannot launder provenance.

--query prints what it matched and asks before sealing: retrieval is fuzzy, and sending the wrong three atoms to a colleague is not undone by an expiry.

--list is how a leaked code announces itself. It shows what you have in flight and whether each has been collected. A bundle you never handed to anybody, marked collected, means somebody else had the code — nothing can un-send it, so treat what was in it as disclosed.

knowl cloud send --list
  2 bundle(s) in flight:
    3f7c…  3 atom(s)  sent 2026-08-14T09:00:00Z  expires 2026-08-15T09:00:00Z
    a19b…  1 atom(s)  sent 2026-08-14T09:04:00Z  collected 2026-08-14T09:31:00Z

knowl cloud send --revoke owl-cascade-ridge-plum-tin
  Revoked. The code is dead and whoever holds it now sees nothing.

--revoke takes either the code or an id from --list. The ids are opaque on purpose: codes are never stored, so the list answers was anything taken, not which of mine was it.

Two kinds of sharing, and they are independent

knowl workspace promote shares an atom with the linked repositories on this machine, by setting its visibility. Nothing leaves the machine.

knowl cloud stage and knowl cloud push share it with the team, over the network, and that state lives in a local ledger rather than in visibility.

Neither implies the other. Promoting publishes nothing, and publishing does not make your other local repositories see it.

knowl cloud stage records an intent and sends nothing. knowl cloud push sends it, from any branch and whatever state your checkout is in.

Only atoms this repository wrote can be published. A reader is refused before anything is sent. A version conflict names the atom and stops rather than overwriting; a detected secret fails the whole batch and is never retried in altered form.

Taking something back

knowl cloud retract <id> --reason "leaked a customer name"

knowl cloud retract removes a published atom from the workspace. The server deletes the row and writes a tombstone in one transaction, then refuses every later publish of that id; teammates lose it on their next sync. It cannot be undone, and the id can never be used again — this is for knowledge that must not remain readable, not for knowledge that stopped being true. Supersede that instead, which keeps the lineage.

It has no branch gate, deliberately. Only drift reports do, because they retire knowledge the rest of the team is relying on. A removal is true from every vantage, and the case that brings you here is a secret sitting in a shared workspace right now — answering that with "switch to the default branch and pull first" would hold the leak open for the length of a rebase.

--reason is required and stored on the tombstone. expectedVersion comes from the local ledger, so if a colleague edited the atom after you published it the retraction stops with a conflict rather than destroying an edit you never read.

From an agent

Once a repository is connected, the MCP server offers one more tool:

  • knowl_cloudaction: "status" reports whether this machine is signed in and as whom, the workspace and your role, how many atoms are queued split into new and corrections, when the background pull is next due, and what a push is currently waiting for. It touches no network, so it answers instantly and offline. action: "stage" is knowl cloud stage: it records an intent and is a dry run unless you pass apply: true. action: "unstage" takes atoms back out of the queue, and is always safe — it sends nothing and unpublishes nothing.

It stops there deliberately. Sending, pulling, connecting and signing in stay yours to run — sending because it is irreversible, the other three because two need a browser and pulling already happens on its own. Asked to send, the agent relays the command instead.

Because knowledge stages itself as it is written, an agent needs a way to say "not this one" at write time: knowl_store takes local: true, the tool-side equivalent of knowl store --local, for knowledge that is only true of this machine.

The local workspace, from an agent

A repository linked into a local workspace offers one more tool:

  • knowl_workspaceaction: "status" names the workspace, this repo's name in it, and every linked repo with whether its database is present. action: "demand" reports what the linked repos have queried each other for, most-repeated first — the readout that says which knowledge this repo owes its peers.

Read-only, on the same line the cloud tool draws. knowl workspace promote is absent because it shares in one step with no second command to complete, so it stays yours; linking and unlinking repos are machine setup and stay yours for the reason knowl init does.

Doing a linked repo's work from here

knowl_store, knowl_decide, knowl_update, knowl_ingest_atoms, knowl_timeline and knowl_evidence_list take an optional repo. Passing it runs that one call as the named repo: against its store, stamped as its own, with its config, its cloud pointer and its ownership rules — exactly as if the command had been run in its directory.

This has never been a new capability, only a newly reachable one. cd ../sibling && knowl store … has always worked, because standing in a repo is what the ownership guard checks — an item there is simply local. An MCP server cannot change directory, so an agent was denied what the human running the same commands could already do, and the workaround was to shell out to the CLI.

It is deliberately full rights, retiring the target's knowledge included. When you are finishing that repo's task, the repo is correcting itself, and which folder your terminal happens to sit in is not a fact about the knowledge. An additive-only version would have left the destructive half reachable only by shelling out, which is the situation this removes.

Three things bound it. The target is named, not pathed — resolved through the workspace manifest, so a repo has to be linked before it can be acted as. A linked repo with no checkout on this machine is refused rather than written to, because a repo's evidence paths and git state do not resolve without a working tree. And repo is honoured only on the tools above: the dispatch reads the published schema, so a tool that does not describe the argument does not accept it, and naming a repo elsewhere refuses the call rather than quietly rebinding it.

That last one matters most on knowl_query, which takes repos — a filter over the shared rows of the repos you name. The singular repo is a rebind, and honouring it there would have read a linked repo's private knowledge as though it were your own.

Use it when the work belongs to the other repo — you are finishing its task and have its context in hand. It is not for correcting something you merely noticed in passing while working here: you have not read that repo's code, and its facts are true of a place you are not standing in.

Staying current

Team knowledge arriving is a notification, not a wait. Queries answer from the replica immediately, a refresh runs in the background, and a TEAM UPDATE: notice tells the agent when something landed that it may want to re-query for. knowl cloud pull forces the refresh when you know a colleague has just published.

Drift is the one upward path that is gated. When a local check finds a published atom's code has moved, it is reported upward from an up-to-date default branch only, and the workspace sees it — so one person noticing protects everyone, and nobody retires an atom over code they simply have not pulled yet.