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
66 changes: 66 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,72 @@

All notable changes to the Toolpath workspace are documented here.

## Share and resume over object storage — 2026-08-25

**`path-cli`** (0.19.0) can write toolpath documents to an S3 bucket, any
S3-compatible endpoint (R2, MinIO, Ceph, B2), or a plain folder — and
read them back.

```bash
path p export object --input claude-abc --to s3://my-bucket/traces
path p export object --input claude-abc --to ~/Dropbox/toolpath-traces
path p import object s3://my-bucket/traces/2026-08-07-fix-the-parser-claude-abc.json

path resume s3://my-bucket/traces/2026-08-07-fix-the-parser-claude-abc.json
path resume ~/Dropbox/toolpath-traces # lists the folder, pick one
```

**Credentials come from wherever you already keep them.** If you use the
AWS CLI, this needs no configuration: `~/.aws/credentials` and
`~/.aws/config` are read directly, `AWS_PROFILE` and `--profile` select
a profile, and the profile's region is used if you haven't set one. SSO,
`role_arn` chains, and `credential_process` profiles are resolved by
shelling out to `aws configure export-credentials` — the AWS CLI's own
resolver, so refresh and every future profile type stay its problem.

When an SSO session has expired, `path` offers to run
`aws sso login --profile <name>` and retries once, rather than making
you go run it and start over. It asks first — that command opens a
browser and waits — and with no terminal to ask, it fails with the exact
command instead of hanging.

`object_store` alone would have covered only the server cases (EC2, ECS,
EKS); it reads no `~/.aws` because it avoids the AWS SDK. Taking on
`aws-config` to fix that would have meant 31 crates and an MSRV
treadmill — its family requires rustc 1.94.1 against a repo pinned to
1.94.0.

`path auth s3 login` is the fallback for endpoints AWS tooling doesn't
know about, where a scoped long-lived token is the right answer. It
stores connection settings at `~/.toolpath/s3.json` (0600) —
deliberately not a destination, so one credential serves any number of
buckets — and merges rather than replaces. `path auth s3 status` reports
which credential source actually won, because that's the first question
when an upload fails.

**Objects are named to be read.** A document lands at
`<date>-<topic>-<cache-id>.json`, e.g.
`2026-08-07-add-s3-support-claude-6f2a1c9e.json`. Every component is a
pure function of the document, so re-exporting a session that grew
overwrites its own object instead of leaving near-duplicates; the date
is the session's *first* step, so it doesn't move as the conversation
continues. That legibility is also what makes browsing cheap:
`path resume <destination>` lists a bucket, prefix, or folder and offers
a picker built from object names alone — no downloads. A destination
holding one document skips the picker.

A scheme-less destination is a **local path**; spell a bucket `s3://`. A
*bare relative* value (`my-bucket/traces`) is rejected rather than
quietly creating `./my-bucket/traces` and reporting success.
`memory://` is rejected too: a fresh per-process store, so anything
written there is gone before the command exits.

Transport is the `object_store` crate, so one code path covers every
backend. The folder backend is what the tests round-trip against, so
export, import, and resume are exercised end to end without a network or
a mock HTTP server.


## `path config edit` — 2026-08-14

- **`path-cli`** (0.18.0): new `path config` porcelain command, starting
Expand Down
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ cargo run -p path-cli -- config edit # $VISUAL/$EDITOR on ~/.toolpath/config.to

The **cache** at `~/.toolpath/documents/<cache-id>.json` is the single landing zone for every `import` (and for `import pathbase` downloads). Cache id is `<source>-<inner-id>` — e.g. `claude-abc123`, `git-main` (Pathbase paths key on `<owner>-<repo>-<slug>`, anon paths on `anon-pathstash-<uuid>`). Files are `0600`, parent directory `0700`. `$TOOLPATH_CONFIG_DIR` overrides the root. Imports error on cache hit (`--force` overwrites); `--no-cache` sends the JSON to stdout for shell composition. `p cache sync` fills the cache incrementally from the installed agent harnesses (see "Things to know") and always overwrites what it re-derives.

**Object-storage transport** lives in `crates/path-cli/src/store.rs`, which splits *where a document goes* (`Destination`, `ObjectUri`, `ObjectName` — pure URL parsing and naming, no credentials) from *how to reach it* (`S3Settings` at `~/.toolpath/s3.json`). Transport is the `object_store` crate, so one code path serves AWS S3, any S3-compatible endpoint (R2, MinIO, Ceph, B2 — set `endpoint`), and `file://` for a local folder; it's async, so it tunnels through the same `cmd_pathbase::block_on` runtime the Pathbase client uses. `file://` is a first-class destination, not a toy — it's what the tests round-trip against, so export/import/resume are covered with no network and no mock HTTP server. Accepted schemes are deliberately narrower than what `object_store` parses (`s3`, `s3a`, `file`): `http`/`https` belong to Pathbase in the same dispatch, `gs://`/`az://` would need feature flags we don't compile in, and `memory://` is a fresh per-process store whose contents vanish before the command exits. A scheme-less destination is a **local path**, and a *bare relative* one (`my-bucket/traces`) is rejected — it's overwhelmingly a bucket name typed from memory, and resolving it against the cwd would create `./my-bucket/traces` and report success.

Object names are `<date>-<topic>-<cache-id>.json` (`store::name_for`): every component is a pure function of the document, so a re-export overwrites its own object rather than leaving near-duplicates (the date is the *earliest* step's, so it doesn't move as the session grows), and the name is legible enough that `Destination::list` + `cmd_resume::pick_from_destination` build picker rows without downloading anything.

**S3 credentials** are resolved by `crate::aws_creds`, not by `object_store` alone. `object_store` covers the *server* cases (EKS/IRSA web identity, ECS task roles, EC2 instance metadata) and deliberately reads no `~/.aws` at all, because it avoids depending on the AWS SDK — which leaves out how nearly every developer actually has S3 access. `aws_creds` fills that in: static-key profiles are parsed straight out of `~/.aws/credentials` (trivial ini, no deps), and anything else — SSO, `role_arn` chains, `credential_process` — is delegated to `aws configure export-credentials --format process`, which runs the AWS CLI's own resolver. Anyone using SSO already has the CLI (`aws sso login` is how they authenticate), and delegating keeps refresh, cache layout, and future profile types the CLI's problem. An expired SSO session is special-cased because it has one obvious fix: `aws_creds` offers to run `aws sso login --profile <name>` and retries **once**, prompting first (the command opens a browser and waits, so it must not fire because someone typed `path resume`) and, with no terminal to ask, failing with the exact command rather than blocking. Detection matches the CLI's message, whose wording varies by version. Depending on `aws-config` instead would be 31 crates *and* an MSRV treadmill: its whole family currently requires rustc 1.94.1 while `rust-toolchain.toml` pins 1.94.0.

Precedence (AWS's own, with our stored settings layered on top): `path auth s3 login` → `--profile` / `$AWS_PROFILE` → `AWS_ACCESS_KEY_ID` → the `[default]` profile → `object_store`'s instance chain. Region falls back to the profile's. `path auth s3 status` prints *which* source won — the first question when an upload fails is always which credential was tried. `AWS_SHARED_CREDENTIALS_FILE` / `AWS_CONFIG_FILE` are honored, which is also how the integration tests stay off a developer's real profiles.

`path auth login` prints `<base>/auth/cli`; the user logs in there and pastes the 8-character code back, which the CLI redeems (`POST /api/v1/auth/cli/redeem`) for a bearer token stored at `~/.toolpath/credentials.json` (`0600`; `$TOOLPATH_CONFIG_DIR` overrides). Server URL comes from `--url`, then `$PATHBASE_URL`, then `https://pathbase.dev`. The redeem endpoint is real but absent from `schema/pathbase-openapi.json` — so the progenitor-derived `pathbase-client` has no `redeem` method; the hand-rolled call in `cmd_pathbase.rs` is the source of truth.

## Key conventions
Expand Down
Loading