Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,30 @@

Written by hand. Each release says what changed for someone running Rowbit, not every commit.

## Unreleased
## 0.2.3 (2026-11-08)

First-run fixes: add Rowbit to a project you already have with `rowbit connect claude-code` run in it, see what Rowbit is connected to with `rowbit status` and the review page's Setup view, and click the links Rowbit prints.

```sh
cd ~/projects/my-app
rowbit connect claude-code
rowbit status
```

### Upgrading from 0.2.2

- `rowbit connect` gives each client entry a token of its own instead of one per agent. An agent's single token from 0.2.2 keeps working; `rowbit agents` lists it with the new ones, and `rowbit agents revoke <agent> --single` removes it once no client uses it. `rowbit agents revoke <agent>` still revokes every token.
- Re-run `rowbit demo` once if you want commands run outside its folder to find it (it now links `~/.rowbit/demo` to its folder); a demo at `~/rowbit-demo` is found without that.

### Added

- `rowbit connect <client>` works from any folder: it uses the folder's own Rowbit files, else `~/.rowbit/default`, else your only setup or the demo, and says which. With several it lists them and asks for `--dir`; with none it says to run `rowbit setup`. Every other command finds its setup the same way.
- Each client entry gets its own token, labelled with where it is ("Claude Code in ~/projects/my-app"), so connecting a second project no longer signs out the first. `rowbit agents` lists them with an id; `rowbit agents revoke <agent> --connection <id>` signs out one.
- `rowbit status` lists every setup on the machine: the database each guards (never its password), whether its review page answers, how many changes are held, each agent's rules and where it is connected.
- The review page says which database it guards in its header, and a Setup view shows the database, Rowbit's folder, sign-in, the agents, their rules and connections.
- Addresses Rowbit prints are clickable links in terminals that support them (Ghostty, iTerm2, WezTerm, kitty, VS Code, Windows Terminal and others); `ROWBIT_HYPERLINKS=0` or `1` overrides. `rowbit demo` opens the review page in the browser once it is up (`--no-open` skips it).
- Agents give the review link with its address as the link text, so it is clickable and shows where it goes.
- The docs look like the home page and link back to it; the README links to the docs on the site.

### Fixed

Expand Down
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "3"
members = ["crates/*"]

[workspace.package]
version = "0.2.2"
version = "0.2.3"
edition = "2024"
rust-version = "1.88"
license = "AGPL-3.0-only"
Expand Down
26 changes: 17 additions & 9 deletions crates/rowbit-cli/src/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ pub fn init(args: Vec<OsString>) -> anyhow::Result<Vec<OsString>> {
}
// `status` lists every setup unless told which.
None if command.as_deref() == Some("status") => None,
// Told where its files are: no setup found elsewhere may lend its
// database URL to them.
None if names_files(&args) => None,
None => {
let found = home().map(|h| setups(&h)).unwrap_or_default();
match pick(&found) {
Expand Down Expand Up @@ -174,6 +177,19 @@ pub fn setups(rowbit_home: &Path) -> Vec<Found> {
/// makes and `rowbit demo clean` removes.
pub const DEMO_LINK: &str = "demo";

/// Whether the command is told where its files are: `--policy`,
/// `--store`, `--config` or their variables.
fn names_files(args: &[OsString]) -> bool {
args.iter().any(|a| {
let a = a.to_string_lossy();
["--policy", "--store", "--config"]
.iter()
.any(|f| a == *f || a.starts_with(&format!("{f}=")))
}) || ["ROWBIT_POLICY", "ROWBIT_STORE", "ROWBIT_SERVER_CONFIG"]
.iter()
.any(|v| std::env::var_os(v).is_some())
}

#[derive(Debug, PartialEq, Eq)]
enum Picked<'a> {
One(&'a Found),
Expand All @@ -198,15 +214,7 @@ fn pick(found: &[Found]) -> Picked<'_> {
/// client name (it lists them) work without one, and so does a command
/// told where its files are (`--policy`, `ROWBIT_STORE`, ...).
fn needs_files(args: &[OsString]) -> bool {
let named = args.iter().any(|a| {
let a = a.to_string_lossy();
["--policy", "--store", "--config"]
.iter()
.any(|f| a == *f || a.starts_with(&format!("{f}=")))
}) || ["ROWBIT_POLICY", "ROWBIT_STORE", "ROWBIT_SERVER_CONFIG"]
.iter()
.any(|v| std::env::var_os(v).is_some());
if named {
if names_files(args) {
return false;
}
let (command, _) = command(args);
Expand Down
8 changes: 4 additions & 4 deletions docs/spec/setup.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Installing and setting up

Status: v0.2.2.
Status: v0.2.3.

How Rowbit gets onto a machine and in front of an agent: the release and its install script, `rowbit setup` for a database of your own, `rowbit demo` for trying it, `rowbit connect` for pointing an agent client at it, the skill that teaches agents to use it, and the home page and docs site that explain it. None of this changes [`rowbit.yml`](policy.md), [`rowbit-server.yml`](server.md) or the state file. The one change to the [change lifecycle](change-lifecycle.md) is the review link in held results (see [Agent tools](change-lifecycle.md#agent-tools-mcp)).

Expand Down Expand Up @@ -82,7 +82,7 @@ It never runs a statement that changes the database or its users. Where somethin

## Where commands find their files

Every command but `demo` and `setup` takes `--dir <path>` (or `ROWBIT_DIR`), after the command's name, and then finds `rowbit.yml`, `rowbit-server.yml`, `rowbit.db` and the `env` file in that directory. Without it, a command uses the current directory if it holds a `rowbit.yml`, `rowbit-server.yml` or `rowbit.db`, and otherwise `~/.rowbit/default` if that holds a setup. Without that either, it looks at every setup in `~/.rowbit` and the demo (the one `~/.rowbit/demo` links to, which `rowbit demo` makes and `rowbit demo clean` removes, or else `~/rowbit-demo`): if there is one, it uses it and says which on standard error; if there are several, it lists them and asks for `--dir`; if there are none, it says to run `rowbit setup`. `ping`, `policy`, `server`, `rowbit connect` without a client, and a command given its files (`--policy`, `--store`, `--config` or their variables) work without one. So `rowbit connect claude-code` works from any project folder. `rowbit setup` refuses a name that is a link, so it never writes into the demo's directory. A flag or variable naming one file (`--policy`, `--store`, `--config` and their variables) still wins for that file. `--dir` before `demo` or `setup` is refused, since they have options of their own.
Every command but `demo` and `setup` takes `--dir <path>` (or `ROWBIT_DIR`), after the command's name, and then finds `rowbit.yml`, `rowbit-server.yml`, `rowbit.db` and the `env` file in that directory. Without it, a command uses the current directory if it holds a `rowbit.yml`, `rowbit-server.yml` or `rowbit.db`, and otherwise `~/.rowbit/default` if that holds a setup. Without that either, and unless `--policy`, `--store`, `--config` or their variables name its files, it looks at every setup in `~/.rowbit` and the demo (the one `~/.rowbit/demo` links to, which `rowbit demo` makes and `rowbit demo clean` removes, or else `~/rowbit-demo`): if there is one, it uses it and says which on standard error; if there are several, it lists them and asks for `--dir`; if there are none, it says to run `rowbit setup`. `ping`, `policy`, `server`, `rowbit connect` without a client, and a command given its files (`--policy`, `--store`, `--config` or their variables) work without one. So `rowbit connect claude-code` works from any project folder. `rowbit setup` refuses a name that is a link, so it never writes into the demo's directory. A flag or variable naming one file (`--policy`, `--store`, `--config` and their variables) still wins for that file. `--dir` before `demo` or `setup` is refused, since they have options of their own.

The database URL is `--url` if given. Otherwise, in a setup's directory, it's the `env` file's, even when `ROWBIT_DATABASE_URL` is set: that may be left in the shell from the demo, and pairing a setup's state with another database would go unnoticed. Rowbit says so on standard error when the two differ. Without an `env` file, `ROWBIT_DATABASE_URL` is used, as in v0.2.1. Rowbit refuses an `env` file that others can read, since it holds the database's password.

Expand Down Expand Up @@ -279,9 +279,9 @@ docker run -i --rm --read-only --cap-drop ALL --security-opt no-new-privileges \

Rowbit is listed in the [MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.Ermag/rowbit`, an OCI package pointing at the image, over stdio. The listing asks for the setup's directory, mounted at `/data`, the agent's id for `mcp --agent`, and the user to run as, and passes the same read-only, no-capabilities flags, so a client installing it runs the command above. That is Rowbit over stdio, started by the agent's client with the setup's files mounted: the database URL and `rowbit.db` are inside the agent's reach, as with any stdio setup (see [Before you use it for real](../quickstart.md#before-you-use-it-for-real)). Agents on the same machine as the person are better served by `rowbit serve` over HTTP, which `rowbit setup` configures. The release workflow checks the listing on every run. `server.json` in the repository holds the listing; the release workflow sets its version to the tag's and publishes it with `mcp-publisher` (checked against its Sigstore signature), signing in through GitHub's OIDC token, after the release and the image are published. The image carries the `io.modelcontextprotocol.server.name` label the Registry checks.

## Not in v0.2.2
## Not in v0.2.3

Windows binaries, signed and notarized macOS binaries, a Homebrew tap, a Claude Desktop `.mcpb` bundle, more than one token per agent, handing out agent tokens or adding database connections from the web UI, named connections (several databases behind one Rowbit, v0.3), client certificates for the database, PostgreSQL, and auditing reads.
Windows binaries, signed and notarized macOS binaries, a Homebrew tap, a Claude Desktop `.mcpb` bundle, tokens that expire, a project-scope `.mcp.json` that reads its token from the environment, handing out agent tokens or adding database connections from the web UI, named connections (several databases behind one Rowbit, v0.3), client certificates for the database, PostgreSQL, and auditing reads.

## Not in v0.2.1

Expand Down
2 changes: 1 addition & 1 deletion plugins/rowbit/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rowbit",
"version": "0.2.2",
"version": "0.2.3",
"description": "Teaches the agent to fix data through Rowbit: one narrow write at a time, and a review link for the person when a write is held. Connect Rowbit itself with `rowbit connect claude-code`.",
"author": {
"name": "Rowbit",
Expand Down
Loading