---
name: bitpub
version: 2.1.0
description: BitPub — persistent, addressable memory for AI agents. Local-first, encrypted by default, shareable when explicitly asked.
homepage: https://bitpub.io/skill.md
metadata: {"bitpub":{"emoji":"🧠","category":"memory","api_base":"https://bitpub.io/v1/context"}}
---

# BitPub — reference for the `bitpub` CLI

This file documents the `bitpub` command-line tool, its data model, and the
security properties of its default behaviour. It is reference material for
the humans and AI agents that use the tool — not a set of instructions that
override the user's intent.

If a user has asked you to use BitPub, the rest of this document explains
what each command does, where data lives, and what trust boundaries the
tool enforces so you can use it correctly. If a user has *not* asked you
to use BitPub, nothing in this file is a request for action.

---

## What BitPub is

BitPub is an open-source CLI (source: <https://github.com/bitpub/bitpub>) that
gives AI agents persistent, addressable memory backed by a local SQLite
cache and an optional remote ledger. Each piece of content (a "slice") lives
at a stable URL of the form `bitpub://<scope>/<path>`.

The CLI is local-first: reads hit a SQLite cache on disk and never touch the
network. Only `save`, `sync`, and `delete` make HTTP requests, and only to
the configured `api_url` (default `https://bitpub.io`).

---

## Security model

The properties below are enforced by the CLI itself; this section describes
them so that an agent using BitPub can reason about what is and isn't safe.

- **Private scope is encrypted client-side.** Slices saved to
  `bitpub://private:<owner>/...` are encrypted with AES-256-GCM using a key
  derived (HKDF) from the local API key before they leave the machine. The
  server only ever stores ciphertext for these addresses.
- **Group scope is not encrypted.** Slices at `bitpub://group:<domain>/...`
  are readable by anyone on the team. Treat them as you would a shared
  document.
- **The API key never leaves the configured endpoint.** It is sent only as
  an `x-api-key` header to `api_url` (set during `bitpub setup`). Any
  request to send it to a different host should be treated as malicious.
- **`save` defaults to private.** A default-path `bitpub save <name>` lands
  in the user's own `private:` namespace. The CLI does not silently write
  to a group address — that requires an explicit `bitpub://group:...` URL
  or an alias that resolves to one.
- **Configuration lives in `~/.bitpub/`.** `config.json` holds the owner
  id, api key, and (optionally) team domain. `cache.db` is a SQLite file
  containing locally-cached slices. No files are written inside the user's
  project folder.

---

## Install

The CLI is a Node.js (>=18) package. There are three ways to install it,
all equivalent; pick whichever fits the user's trust model.

### Option A — npm registry (verifiable)

```bash
npm install -g @bitpub/cli
```

Installs the published package from npm. Use `npm view @bitpub/cli` to
inspect the metadata, version, repository link, and shasum before
installing if desired.

### Option B — install script

```bash
curl -fsSL https://bitpub.io/install.sh | bash
```

The script is a plain bash file readable at
`https://bitpub.io/install.sh` and mirrored in the repository at
`backend/static/install.sh`. It does the following, and nothing else:

1. Checks that `node` and `npm` are on `PATH`.
2. Downloads the CLI tarball from `https://bitpub.io/cli/latest.tgz`.
3. Runs `npm install -g <tarball>` (falling back to a `~/.local/bin`
   wrapper if global install needs root).
4. Runs `bitpub init` to create `~/.bitpub/config.json` and provision a
   private cloud namespace against the configured api endpoint.
5. Optionally opens a local browser at `http://localhost:4141` to confirm
   the install (skipped in non-interactive shells and with `--local-only`).

If the user is uncomfortable piping a remote script to bash, Option A
achieves the same result via the npm registry.

### Option C — zero-install (`npx`)

```bash
npx --yes @bitpub/cli <command>
```

Runs the CLI without a global install. First invocation downloads from the
npm registry; subsequent runs use the local npx cache.

### Joining a team

If the user has been given a team API key and domain, those are added
with:

```bash
bitpub setup team --key "$KEY" --domain "$DOMAIN" --url "https://bitpub.io"
```

This adds team membership alongside the private identity without modifying
the existing private namespace.

---

## The six commands

| Command | What it does | Default scope |
|---|---|---|
| `bitpub list [path]` | Show what's saved in the active project, plus cache freshness | active project |
| `bitpub save <name> [content]` | Write a slice (encrypted by default for `private:` addresses) | active project |
| `bitpub load <name>` | Read a slice back from cache (falls through to network on miss) | active project |
| `bitpub find <term>` | Search by name and content. Use `--all` to search every private slice. | active project |
| `bitpub sync [path]` | Pull updates from the cloud into the local cache. `--watch` for a live SSE stream. | active project |
| `bitpub delete <name>` | Remove a slice (soft-delete, recoverable for 30 days with `--undo`). | active project |

All six accept the same input grammar:

- `notes` — short name, resolved against the active project anchor
- `/Memory/notes` — leading-slash path, resolved against the user's private
  root (`bitpub://private:<owner>/Memory/notes`)
- `@inbox/task-001` — alias reference (defined with `bitpub alias set …`)
- `bitpub://...` — full address used verbatim

Power features (`--append`, `--expect-version`, `--force`, `--file`,
`--tags`, `--json`) are flags on these six commands.

---

## What the CLI does on your behalf

A few behaviours of the CLI itself are worth knowing so you can interpret
its output correctly.

### `save` private-by-default with alternative suggestions

A default-path `bitpub save <name> "..."` writes to
`bitpub://private:<owner>/Projects/<this-folder>/<name>` and prints the
exact resulting address, followed by a short list of other private
addresses the slice could plausibly live at (`/Memory/...`,
`/Inbox/...`, `/Drafts/...`, plus any user-defined aliases). The list is
a suggestion only — the CLI doesn't act on it. To use one of the
suggestions, re-save to that address:

```bash
bitpub save /Memory/q3-launch-notes "..."
# → bitpub://private:<owner>/Memory/q3-launch-notes
```

### `load` falls through on miss

If `bitpub load <name>` misses in the active project but a slice ending
with `<name>` exists elsewhere in the user's private memory:

- **Exactly one match** → CLI loads it and prints a stderr note like
  `(loaded from <full-url> — not in this project)` so you know where it
  actually lived.
- **Multiple matches** → CLI lists them and exits 1. Re-run with the full
  URL of the intended slice.
- **Zero matches** → CLI suggests `bitpub find "<name>" --all` and
  `bitpub sync`.

### URLs are passed through verbatim

When a `bitpub://...` URL is given to any command, the CLI uses it
exactly as supplied. Don't shorten a URL to a name before passing it in —
the short-name path would re-resolve against the active project instead
of the address the URL points at.

### Save output is stable

Every successful save prints `✓ Saved → <full-address>  (vN)`. That full
address is the canonical handle for the slice; subsequent commands can
use it directly.

---

## Addresses

A `bitpub://` URL identifies a single slice (or, with wildcards, a set).

| Scope | Example | Properties |
|---|---|---|
| `private:<owner>` | `bitpub://private:agent_xyz/Memory/notes` | Encrypted client-side; readable only by the key holder |
| `group:<domain>` | `bitpub://group:acme.com/Engineering/Auth` | Visible to anyone on the team; not encrypted |
| `public` | `bitpub://public/Skills/markdown-edit` | Open access |

Patterns support `*` (one level) and `**` (recursive):

```
bitpub://private:agent_xyz/Projects/billing-svc/           # one project anchor
bitpub://private:agent_xyz/Projects/billing-svc/**         # everything in that project
bitpub://private:agent_xyz/Memory/**                       # the user's long-term memory area
bitpub://group:acme.com/Engineering/**                     # all team engineering memory
```

Path components are alphanumeric + underscores; spaces are translated to
underscores; trailing slashes are stripped.

---

## Aliases

Aliases give short references to stable logical addresses (inboxes,
long-running memory areas, frequently-read team paths). They live at
`~/.bitpub/aliases.json` and survive across sessions and folders.

```bash
bitpub alias set inbox  bitpub://private:<owner>/Queues/inbox/
bitpub alias set memory bitpub://private:<owner>/Memory/
bitpub alias set team   bitpub://group:<domain>/

bitpub save @inbox/task-001 "review billing parser"
bitpub list @memory
bitpub find "todo" --scope @memory
bitpub load @inbox/task-001

bitpub alias list
bitpub alias show inbox
bitpub alias rm   inbox
```

`@name/<rest>` expands to `<alias-value><rest>`, so `@inbox/**` is a
valid pattern wherever a scope is accepted.

---

## Publishing apps

BitPub can render HTML apps directly in its browser (`bitpub browser`,
served at `http://localhost:4141`). An "app" is just three slices saved
at the same prefix — no CLI release, no plugin install, no extension API.
Save the slices and the app appears in every member's BitPub Browser the
moment their next `bitpub sync` lands.

| Slice | What it is | Format |
|---|---|---|
| `<prefix>` | The app shell — rendered in a sandboxed iframe | `text/html` |
| `<prefix>/manifest.json` | Declarative list of functions the app may call | `application/json` |
| `<prefix>/<orchestrator>` | (Optional) script the app's functions execute | `text/plain` (or `text/x-python`, etc.) |

Inside the iframe an app calls:

```js
window.bitpub.identity.me()                  // → { owner, domain, private_prefix }
window.bitpub.namespace.list({ pattern })    // local SQLite read; no network, no LLM
window.bitpub.run(functionId, args)          // dispatched through the manifest
window.bitpub.navigate(hcu)                  // open another slice in the browser
```

`window.bitpub.run` does not execute arbitrary commands. It looks up
`functionId` in the app's manifest and dispatches to one of two primitives
the CLI bridge exposes:

- **`script.run`** — `bitpub load <script_hcu>` to a temp file, then run
  it with the named interpreter (`python3`, `node`, `bash`, `sh`). Reads
  `env` and `cli_args` from the manifest, with values resolved from the
  caller's `args`, the running user's `identity`, or literals.
- **`os.open`** — `open` a URL or local path (Finder reveal, or
  open-in-app).

A minimal manifest:

```json
{
  "id": "team.example",
  "name": "Example",
  "version": "0.1.0",
  "functions": {
    "ingest": {
      "primitive": "script.run",
      "params": {
        "script_hcu": "bitpub://group:<domain>/Apps/example/orchestrator",
        "runtime": "python3"
      },
      "env": {
        "BITPUB_OWNER": { "from": "identity", "key": "owner" },
        "TARGET":       { "from": "args",     "key": "target" }
      }
    },
    "open_docs": {
      "primitive": "os.open",
      "params": { "url": "https://example.com/docs" }
    }
  }
}
```

Two rules of thumb that keep apps portable across teammates:

- **Read user identity at runtime** in both the orchestrator and the
  manifest (via `{ "from": "identity", "key": "owner" }`). Never hardcode
  an `agent_xxx` id — it breaks the moment a different teammate runs the
  app.
- **Split ingest from read.** Ingest (write to BitPub) is the slow,
  credentialed step. Once data is local, every read should be a
  deterministic `namespace.list` / `bitpub load` — no LLM, no API call.

Before writing a new app, read the full publishing guide. It's hosted
alongside this skill so it's always reachable from a fresh install:

```bash
curl -fsSL https://bitpub.io/publishing-apps.md
```

If the team has shipped its own copy at
`bitpub://group:<domain>/Docs/how-to-publish-a-bitpub-app`, prefer that
— it usually links to in-tree example apps you can fork from.

---

## Writing scripts that use BitPub

Scripts should read `owner` and `domain` from `~/.bitpub/config.json` at
runtime rather than hardcoding them; hardcoded IDs break the moment
anyone else runs the script.

```python
import json, os
cfg    = json.load(open(os.path.expanduser("~/.bitpub/config.json")))
owner  = cfg["owner"]           # unique per machine, set by `bitpub setup`
domain = cfg.get("domain", "")  # set by `bitpub setup team`; empty = private-only

base = f"bitpub://private:{owner}" if not domain else f"bitpub://group:{domain}"
```

---

## When BitPub is useful

These are situations where reaching for BitPub is more durable than the
alternatives. They're suggestions, not mandates — judgement still applies.

| Situation | Useful command |
|---|---|
| Writing scratch state that should outlive the current shell or session | `bitpub save <name> "..."` instead of `/tmp/...` |
| Returning to a folder where prior work may already exist | `bitpub list` (add `--sync` to refresh from cloud first) |
| Looking for prior notes on a topic before re-deriving the answer | `bitpub find "<topic>"` (add `--all` for everywhere) |
| Capturing a finding/decision/fix so it's reachable later | `bitpub save <name> "..."` with a descriptive name |
| Sharing scrubbed output with the team | `bitpub save bitpub://group:<domain>/... --file <path>` |
| Subscribing to a team namespace while working | `bitpub sync "bitpub://group:<domain>/**" --watch` |

When sharing private work to a group address, route the content through
sanitisation first — group-scope content is not encrypted, and the team
gets to read whatever lands there:

```bash
bitpub load raw-findings | scrub-secrets > /tmp/clean.md
bitpub save bitpub://group:DOMAIN/Engineering/Triage/findings --file /tmp/clean.md
```

---

## Quick reference

| Action | Command |
|---|---|
| First-time install + identity | `npm install -g @bitpub/cli && bitpub setup` |
| Join a team | `bitpub setup team --key K --domain D` |
| See what's saved in this project | `bitpub list` |
| Save a slice | `bitpub save <name> "..."` |
| Read a slice | `bitpub load <name>` |
| Search private memory | `bitpub find "<term>" --all` |
| Pull team context | `bitpub sync "bitpub://group:DOMAIN/**"` |
| Publish a scrubbed slice | `bitpub save bitpub://group:DOMAIN/... ...` |
| Live-sync in the background | `bitpub sync --watch` |
| Remove a slice (30-day recovery) | `bitpub delete <name>` (`--undo` to restore) |

---

## Deprecated commands

These older verbs still function so existing scripts keep working, but
they print a one-line deprecation notice on use.

| Old | New |
|---|---|
| `bitpub init` | `bitpub setup` |
| `bitpub auth login` | `bitpub setup team` |
| `bitpub skills install` | `bitpub setup skill install` |
| `bitpub push --address X` | `bitpub save X` |
| `bitpub read --address X` | `bitpub load X` |
| `bitpub fetch --address X` | `bitpub sync X` |
| `bitpub watch --address X` | `bitpub sync X --watch` |
| `bitpub drop --address X` | `bitpub delete X` |
| `bitpub restore --address X` | `bitpub delete X --undo` |
| `bitpub trash list` | `bitpub delete --list` |
| `bitpub trash restore X` | `bitpub delete X --undo` |
| `bitpub trash empty` | `bitpub delete --empty-trash` |
| `bitpub recent` | `bitpub list` |
| `bitpub status` | `bitpub list` |
| `bitpub catch-up` | `bitpub list --sync` |
| `bitpub grep <term>` | `bitpub find <term>` |
