Metadata-Version: 2.4
Name: memfleet
Version: 2.0.3
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
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

```bash
pipx install memfleet          # once strata-mem + memfleet are on PyPI
```

**Release order** (`strata-mem` is not on PyPI yet): the engine publishes
`strata-mem` **1.6.2** first (it carries the #113 judge-parse fix and exposes
`strata.install`), then this client publishes `memfleet` **2.0.0**, which
depends on it. Until `strata-mem` is on PyPI, install the engine from source or
git first:

```bash
pip install "strata-mem @ git+https://github.com/oren198/Strata.git"
pip install -e client/          # from a strata-web checkout
```

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

> The contribution Stop-hook + evaluator script (Strata#112) does not exist
> yet; this client writes **no** hook block. There is a marked extension point
> for it in `memfleet/setup.py` (`TODO(strata#112)`).

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