Metadata-Version: 2.4
Name: memfleet
Version: 2.0.8
Summary: The memfleet cloud client — connect a terminal to a Strata fleet on memfleet.com without leaving it.
Requires-Python: >=3.11
Requires-Dist: httpx
Requires-Dist: keyring
Requires-Dist: platformdirs
Requires-Dist: strata-mem>=1.6.1
Requires-Dist: tomli-w
Provides-Extra: dev
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# memfleet

**`memfleet`** is the cloud client for a [Strata](https://github.com/oren198/Strata)
fleet on **memfleet.com**. `strata` is the local memory engine; `memfleet` is
the remote fleet cloud. A developer connects a terminal to the cloud without
ever leaving it:

```
pipx install memfleet
cd my-project && memfleet connect      # one browser approval, the rest in-terminal
memfleet run claude                    # the session binds this project's profile
```

…and a teammate who only holds an **enrollment code** reaches the same with:

```
memfleet connect --code XXXX
```

> ### A note on the version history
> Releases of the `memfleet` distribution **at or below 1.6.1** were the Strata
> *engine* — that package has been renamed and now ships as
> [`strata-mem`](https://pypi.org/project/strata-mem/) (import package and CLI
> still `strata`; ADR 0009). **`memfleet` 2.0.0 is the first release of the
> cloud client** described here. The major-version jump is deliberate: the
> repurposed package's "latest" must never resolve to an old engine release.

## Install

Requires **Python 3.11+** (stdlib `tomllib`, and the `strata-mem` dependency
is 3.11+).

```bash
pipx install memfleet
# older default interpreter? point pipx at a newer one:
pipx install --python python3.11 memfleet
```

Depends on [`strata-mem`](https://pypi.org/project/strata-mem/) (the Strata
engine's install machinery); pip resolves it automatically. A `No matching
distribution found for memfleet` error from pip almost always means the
installing interpreter is older than 3.11 — note that **pipx uses its own
interpreter, not your shell's**: an apt-installed pipx on Ubuntu 22.04 runs
the system 3.10 even inside an activated 3.11 venv. Use
`pipx install --python "$(which python)" memfleet`, or plain
`pip install memfleet` inside any 3.11+ venv.

## Quick start

```
pipx install memfleet
cd your-project && memfleet connect   # one browser approval
memfleet run claude                   # session up, bound to your scope
```

## Operation modes

| How often | Command | What happens |
|---|---|---|
| Once per machine | first `memfleet connect` (or `memfleet login`) | Device-flow browser approval; the **owner token** lands in the OS keychain. Never repeated. |
| Once per project | `memfleet connect` | Registers the agent, writes the secret-free project setup (MCP config, contribution skill, Stop-hook), saves the **agent profile** as the project default. |
| Every session | `memfleet run claude` | Starts `claude` with the **agent key** injected into that one process. |

`memfleet run` is a generic launcher — the last argument is the command it
starts (`claude`, `bash`, anything). It resolves the active profile, pulls the
agent key from the keychain, and exec's the command with `MEMFLEET_AGENT_KEY`
set in **that process's environment only**. The key never touches a file or
your shell, and dies with the process; it is what the committed `.mcp.json`'s
`${MEMFLEET_AGENT_KEY}` resolves at session start.

### Connecting more agents

- **Another project** — `cd` there and run `memfleet connect` again. No
  browser step: the owner token is already in the keychain.
- **A second agent in the same project** (say, a different scope) —
  `memfleet connect --scope <scope-id> --name <agent-name>`. Every connect
  saves its own profile (`<workspace>/<agent>`); the newest becomes the
  project default.
- **Choose per session** — `memfleet run --profile "Fleet/other-agent" claude`.
  Two terminals on the same repo can run as different agents concurrently
  (**sessions bind, not machines** — nothing is exported globally).
- **A teammate without owner access** — `memfleet connect --code XXXX` with an
  enrollment code you mint from the Console (no owner login involved).

## Commands

| Command | What it does |
|---|---|
| `memfleet login` / `logout` | Device-flow login; the **owner token** goes to the OS keychain. |
| `memfleet connect` | Guided: log in → pick/create a **workspace** → pick/define a **scope** → create an agent → save the **agent profile** → wire the project up → verify. |
| `memfleet connect --code XXXX` | Enrollment-code path — no owner login (delegation plane). |
| `memfleet workspaces list \| create` | Manage workspaces over `/api/v1/manage/*`. |
| `memfleet scopes list \| create` | List / define scopes (validated engine-side). |
| `memfleet agents list \| create \| revoke` | Manage Registered Agents. |
| `memfleet codes list \| create \| revoke` | Manage enrollment codes. |
| `memfleet profiles list \| use` | List agent profiles; `use` writes the project default. |
| `memfleet run [--profile X] COMMAND…` | Exec COMMAND with the resolved profile's **agent key** injected into its environment (per-process; the recommended way to start `claude`). |
| `memfleet env [--profile X]` | Print eval-able `export` lines (shell/CI bridge; prints the key to stdout by design). |
| `memfleet setup` | (Re)write the additive project setup on its own. |

## The binding model

Three credential planes, **never crossed** (ADR 0009 D4):

- the **owner token** (`sot_…`) — device-flow issued, manages the fleet;
- the **agent key** (`sak_…`) — what a session presents; one agent ⇄ one
  `(scope, skill)` binding, fixed at registration;
- the **enrollment code** (`sge_…`) — delegation to non-owners.

**Sessions bind, not machines.** Locally there are only named **agent
profiles** (`<workspace>/<agent>`); the binding lives server-side. One machine
may hold many profiles and each concurrent session resolves its own.

### Secrets never touch disk

Secrets live **only** in the OS keychain (via `keyring`) or the environment —
the client never writes an owner token or an agent key to a file:

- the **owner token** is keyed by server URL;
- each **agent key** is keyed by its profile name.

Where no keychain backend exists (headless, CI, containers), secrets come from
the environment instead:

- `MEMFLEET_AGENT_KEY` — the agent key for the active profile (the same name the
  written MCP config resolves at session start);
- `MEMFLEET_OWNER_TOKEN` — an owner token for the management plane.

In that mode a *write* cannot persist without putting a secret on disk, which
the client refuses to do; it prints the env var to set instead.

## What `connect` writes (additive, idempotent, secret-free)

Setup is **strictly additive** and reuses the engine's install machinery
(`strata.install`, ADR 0009 D3) — an existing entry is never overwritten. Run
with `--diff` to preview without writing; a second run is a no-op.

- an **MCP config** entry for the hosted `/mcp` endpoint, keyed `memfleet`
  (distinct from the local engine's `strata` entry, so a project may run both).
  It is **secret-free**: the agent key is resolved at session start from the
  profile env (`MEMFLEET_AGENT_KEY`) —

  ```json
  {
    "mcpServers": {
      "memfleet": {
        "type": "http",
        "url": "https://memfleet.com/mcp",
        "headers": { "Authorization": "Bearer ${MEMFLEET_AGENT_KEY}" }
      }
    }
  }
  ```

- the **`memfleet-contribution` skill** into `.claude/skills/`.

- the **contribution Stop-hook** — the script at
  `.claude/hooks/memfleet-stop-hook` plus its `hooks.Stop` wiring merged into
  `.claude/settings.json`. Both blocks are server-rendered and fetched at
  connect time; dependency-free and secret-free, the hook reads the agent key
  from the environment and degrades to a no-op when none is present. (Against
  a server that does not serve setup blocks, connect notes the skip and
  everything else proceeds.)

The MCP entry is written to `.mcp.json` at the project root (the file where Claude Code expands `${VAR}` in HTTP headers); it is secret-free and safe to commit. `--project` governs the repo-committable framing of future artifacts
(commit it); the default writes the machine-local `.claude/settings.local.json`.
Both are secret-free — a committed config with no profile present simply fails
to authenticate the MCP server, and the session degrades silently.

## Machines without an OS keychain (headless / CI / containers)

`connect` still completes: the setup is written as usual and the credentials
are shown ONCE as `export` lines at the end (nothing secret is ever written to
disk). Set them in the session environment — `memfleet run` passes an ambient
`MEMFLEET_AGENT_KEY` through — or re-run `connect` for fresh ones.
`memfleet profiles list` marks such profiles `(key: env)`.

## Configuration precedence

- **server** — `--server` flag → `MEMFLEET_SERVER` env → project
  `.memfleet/config.toml` `server` → default `https://memfleet.com`. No URL
  appears in the normal path; `--server` is for test / self-hosted only.
- **agent profile** — `MEMFLEET_PROFILE` env → project `.memfleet/config.toml`
  `default_profile`. The env override lets concurrent same-repo sessions each
  resolve their own profile.

`.memfleet/config.toml` holds only non-secret project state (a server URL and a
default profile name). It is `.memfleet/`, not `.strata/`, so a project may run
a local `strata` engine and the cloud side by side.
