From 607d829fcf9a6fbf56f4847c9c43b1f31d1008f2 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 21 Jul 2026 20:13:20 +0000 Subject: [PATCH] docs: rewrite the README as a landing page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README read as a manual: 186 lines / 1273 words, of which Secrets alone was 62 lines and 489 words — 38% of the document, more than Why + Status + Install combined. A backend-selection matrix containing a D-Bus error string sat where every visitor had to scroll past it. Benchmarked against the READMEs of comparable projects (bruno, yaak, zed, helix, posting, atuin, httpie, ripgrep, bat, hoppscotch). Two archetypes emerged: CLI tools whose README *is* the manual (bat 4.6k words, ripgrep 2.9k), and GUI apps whose README is a landing page and whose docs teach (yaak 494, zed 298, posting 256, helix 195). Posel has a GUI and a docs/ directory, so it's the latter; it was sitting at 2.6x yaak. Now 127 lines / 624 words. Changes: - Leads with one claim instead of four, then three standalone sentences ending on an absolute ("Not now, not later") — the line people quote. - Shows a real request file inline: examples/github-api/User/get-a-user.toml, byte-identical, variables and assertions visible in 13 lines. "Your requests are files" is abstract; the snippet is not, and no comparable project does this. - Surfaces what was undersold: tRPC (the repo description already advertised it, the README didn't), assertions + `posel-cli test`, OpenAPI/curl import, codegen. - Adds a "Why shouldn't I use Posel?" section — pre-1.0, unsigned builds, no Postman-collection import. Preempts the first comment on any launch thread. - Four badges: CI, release, license, Rust. No star or download counts. - Moves Secrets to docs/SECRETS.md (6 lines remain), the crate table to CONTRIBUTING.md, and build/GPUI notes there too. Adds CONTRIBUTING.md, which the repo didn't have: build, Linux prerequisites, crate layout, the gpui pin rationale, and the CI gate to run before pushing. --- CONTRIBUTING.md | 78 +++++++++++++++++ README.md | 229 ++++++++++++++++++------------------------------ docs/SECRETS.md | 62 +++++++++++++ 3 files changed, 225 insertions(+), 144 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 docs/SECRETS.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0d29535 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,78 @@ +# Contributing + +Thanks for taking a look. Posel is pre-1.0 and moving quickly — issues and PRs are welcome. + +## Build & run + +Requires a current stable Rust toolchain. GPUI uses recently-stabilized std features; if a build +fails with `E0658`, run `rustup update stable`. + +```bash +cargo run -p posel-app # the desktop app (binary: posel) +cargo run -p posel-cli -- # headless engine driver +cargo test --workspace +cargo clippy --workspace --all-targets -- --deny warnings +cargo fmt --all --check # CI enforces this +``` + +A workspace can be opened by passing a path as the first argument to the app, or via the +`POSEL_WORKSPACE` environment variable: + +```bash +cargo run -p posel-app -- examples/github-api +``` + +### Linux build prerequisites + +GPUI links against system libraries that need their development packages present. Without them the +build fails inside `yeslogic-fontconfig-sys` with *"The pkg-config command could not be found"*, +which reads like a toolchain problem but isn't: + +```bash +# Debian / Ubuntu +sudo apt install -y pkg-config libfontconfig-dev libfreetype-dev \ + libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev libxcb1-dev libx11-dev +``` + +## Layout + +The domain logic is headless and unit-tested; GPUI is confined to the edge (`ui`/`app` only). + +| Crate | Responsibility | +|-------|----------------| +| `core` | Protocol-tagged request/collection model (the source of truth) | +| `template` | Variable / secret / dynamic-value resolution | +| `engine` | tokio runtime, the `Transport` trait + HTTP transport, request resolution | +| `schema` | GraphQL brains: introspection → queryable schema, completion, formatting, validation | +| `storage` | git-friendly `.toml` format, secret backends, history, file-watching | +| `ui` | GPUI widgets: editors, viewers, sidebar, palette (protocol-aware via enum dispatch) | +| `app` | `posel` desktop binary | +| `cli` | `posel-cli` headless engine driver | + +`core`, `template`, `engine`, `schema`, and `storage` carry no UI dependency and are tested in +isolation. A new protocol is a new `Transport` impl plus a registered editor/viewer; the UI stays +protocol-agnostic. See [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md). + +## A note on the GPUI dependency + +GPUI ships only as a git dependency (no crates.io release), and Posel carries a handful of small, +upstreamable editor enhancements to `gpui-component` — a completion trigger, bracket-aware +auto-indent, and bracket/quote auto-pairing among them. Those live on a public fork, +[`starvy/gpui-component`](https://github.com/starvy/gpui-component), pinned here to an immutable tag +so the whole graph resolves to a single, reproducible `gpui`. No local checkout is needed — +`cargo build` fetches everything. + +Because the fork pins `gpui` itself, the two must move in lockstep: bumping one without the other +gives you two incompatible copies of `gpui` in the graph. + +## Conventions + +- **Conventional Commits** — `type(scope): subject`, scope is the crate (`ui`, `engine`, `storage`, …). +- **GPUI at the edge.** Only `ui` and `app` may depend on `gpui`/`gpui-component`. +- **Never block the GPUI thread.** IO runs on the engine's tokio runtime and is marshaled back + via channels. +- `thiserror` in libraries, `anyhow` at binary edges. No `unwrap`/`expect` on fallible library paths. +- Tests track behavior, not lines — cover engine/storage/template logic and tricky edge cases. + +Before pushing, run the same gate CI does: `cargo fmt --all --check`, `cargo clippy --workspace +--all-targets -- --deny warnings`, `cargo test --workspace`. diff --git a/README.md b/README.md index 427028e..c139e8d 100644 --- a/README.md +++ b/README.md @@ -1,186 +1,127 @@ # Posel -A fast, **native** API client for developers — REST and GraphQL first, multi-protocol by -design. No webview, no account, no cloud. Your collections are plain files on disk you can -commit to git. +**A native API client for REST, GraphQL, and tRPC. Your requests are files.** -Built in Rust on [GPUI](https://www.gpui.rs/) (the framework behind the Zed editor) and the -[gpui-component](https://github.com/longbridge/gpui-component) widget library. +[![CI](https://github.com/starvy/posel/actions/workflows/ci.yml/badge.svg)](https://github.com/starvy/posel/actions/workflows/ci.yml) +[![Release](https://img.shields.io/github/v/release/starvy/posel)](https://github.com/starvy/posel/releases/latest) +[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](#license) +[![Built with Rust](https://img.shields.io/badge/built%20with-Rust-dea584?logo=rust&logoColor=white)](https://www.rust-lang.org) -![Posel — sending REST and GraphQL requests, switching environments, and inspecting response timing](docs/assets/posel-demo.gif) +Posel stores your requests as plain `.toml` files in a folder you own. + +Diff them, review them, branch them — collaboration is just git. -Opening a saved request, sending it with Ctrl+Enter, resolving -`{{variables}}` against an environment, inspecting headers and the timing waterfall, running a -GraphQL query, and the command palette. ([higher-quality MP4](docs/assets/posel-demo.mp4)) +No account, no sync server, no telemetry. Not now, not later. -## Why another API client? +![Posel — sending REST and GraphQL requests, switching environments, and inspecting response timing](docs/assets/posel-demo.gif) -Existing clients are mostly either cloud-first and heavy, or built on a web runtime. Posel's bet -is the opposite corner of the design space: +[Download](https://github.com/starvy/posel/releases) · [Docs](./docs) · [Architecture](./docs/ARCHITECTURE.md) · [Contributing](./CONTRIBUTING.md) -- **Native speed & huge payloads** — responses stream on a tokio runtime and render - incrementally; no webview to choke on large bodies. -- **Multi-protocol depth** — REST + GraphQL today, behind a clean *Protocol vs Transport* - boundary so gRPC and WebSocket/SSE slot in additively rather than as bolt-ons. -- **Git-native & local-first** — collections are human-readable `.toml`; collaboration is just - git. Secrets stay out of the repo — in your OS keychain by default, or another backend you - pick ([Secrets](#secrets)). -- **Keyboard-driven / Zed-like** — command palette, quick-open, env switcher, multi-tab; a - keyboard-first flow with minimal chrome. +## Your requests are files -## Status +This is a whole request — variables, and tests that run on every send: + +```toml +name = "Get a user" +method = "GET" +url = "{{base_url}}/users/{{username}}" + +[[assertions]] +target = "status" +op = "equals" +value = "200" + +[[assertions]] +target = "json" +path = "login" +op = "equals" +value = "{{username}}" +``` -Working, in active development. Runs on **macOS, Linux, and Windows**. +Commit it. Review it in a PR. Run it in CI with `posel-cli test`. -- **REST** — request editor (method, query/headers/body), streaming response panel, - environments + variables, pluggable secret storage, request history. -- **GraphQL** — schema introspection from the endpoint, schema-aware autocomplete, query - formatting, inline validation, and a dedicated data/errors viewer. -- **Workspace** — git-friendly `.toml` collections, live reflection of external file edits, - full collection CRUD, multi-tab requests, command palette. -- **Headless CLI** — `posel-cli` drives the same engine without the UI (REST send, GraphQL - introspect/query). +## Features -See [docs/PLAN.md](./docs/PLAN.md) for the phased roadmap and what's next. +- **REST, GraphQL, and tRPC** — GraphQL gets schema introspection, schema-aware autocomplete, + formatting, and inline validation. +- **Native, not a webview** — responses stream on a tokio runtime and render incrementally, so a + 100 MB body opens and scrolls without jank. +- **Environments & variables** — `{{base_url}}` per environment, with a switcher. +- **Secrets stay out of the repo** — only the `{{secret:name}}` reference is written to disk. +- **Assertions & a headless CLI** — `posel-cli test` runs the same requests in CI. +- **Import & export** — bring in OpenAPI 3.x specs or a `curl` command; copy any request back out + as curl or JS fetch. +- **Keyboard-first** — command palette, quick-open, env switcher, multi-tab. Ctrl+Enter sends. ## Install -Grab the latest build for your platform from the -[Releases](https://github.com/starvy/posel/releases) page: +Grab a build from the [Releases](https://github.com/starvy/posel/releases) page: -- **macOS** — `.dmg` (universal: Apple Silicon + Intel) -- **Linux** — `.AppImage` (portable) or `.deb` (Debian/Ubuntu) -- **Windows** — `.msi` installer +| Platform | Download | +|----------|----------| +| macOS | `.dmg` (universal — Apple Silicon + Intel) | +| Linux | `.AppImage` (portable) or `.deb` | +| Windows | `.msi` | -> **Unsigned builds.** Releases aren't code-signed yet, so the OS warns on first launch: -> - **macOS** — right-click the app → **Open**, or `xattr -dr com.apple.quarantine /Applications/Posel.app`. -> - **Windows** — SmartScreen → **More info** → **Run anyway**. -> -> This goes away once signing/notarization is set up. +
+First launch on macOS or Windows -## Architecture +Releases aren't code-signed yet, so the OS warns the first time: -The domain logic is headless and unit-tested; GPUI is confined to the edge (`ui`/`app` only). +- **macOS** — right-click the app → **Open**, or + `xattr -dr com.apple.quarantine /Applications/Posel.app` +- **Windows** — SmartScreen → **More info** → **Run anyway** -| Crate | Responsibility | -|-------|----------------| -| `core` | Protocol-tagged request/collection model (the source of truth) | -| `template` | Variable / secret / dynamic-value resolution | -| `engine` | tokio runtime, the `Transport` trait + HTTP transport, REST & GraphQL resolution | -| `schema` | GraphQL brains: introspection → queryable schema, completion, formatting, validation | -| `storage` | git-friendly `.toml` format, pluggable secret backends, history, file-watching | -| `ui` | GPUI widgets: editors, viewers, sidebar, palette (protocol-aware via enum dispatch) | -| `app` | `posel` desktop binary | -| `cli` | `posel-cli` headless engine driver | +This goes away once signing and notarization are set up. -`core`, `template`, `engine`, `schema`, and `storage` carry no UI dependency and are tested in -isolation. New protocols are a new `Transport` impl plus a registered editor/viewer; the UI -stays protocol-agnostic. See [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md). +
-## Build & run +Or build it yourself — see [CONTRIBUTING.md](./CONTRIBUTING.md). -Requires a current stable Rust toolchain (GPUI uses recently-stabilized std features; run -`rustup update stable` if a build fails with `E0658`). Builds on macOS, Linux, and Windows; -secrets default to the platform keychain (macOS Keychain, Windows Credential Manager, Linux -Secret Service) and can be pointed elsewhere — see [Secrets](#secrets). +## Quick start -On **Linux**, GPUI links against system libraries that need their development packages present, -or the build fails in `yeslogic-fontconfig-sys` with *"The pkg-config command could not be -found"*: +There's a ready-made workspace in [`examples/github-api`](./examples/github-api): 36 requests across +seven collections, REST and GraphQL, two environments — and a `Meta` collection that needs no token. ```bash -# Debian / Ubuntu -sudo apt install -y pkg-config libfontconfig-dev libfreetype-dev \ - libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev libxcb1-dev libx11-dev +cargo run -p posel-app -- examples/github-api # or File ▸ Open Workspace… ``` +Pick **Meta ▸ Get zen** and hit Ctrl+Enter. Same thing headless: + ```bash -cargo run -p posel-app # the desktop app (binary: posel) -cargo run -p posel-cli -- # headless engine driver -cargo test --workspace -cargo clippy --workspace -- --deny warnings +posel-cli send examples/github-api Meta/get-zen.toml +posel-cli test examples/github-api Meta/get-zen.toml # run its assertions ``` -A workspace can be opened by passing a path as the first argument to the app, or via the -`POSEL_WORKSPACE` environment variable. - ## Secrets -A `{{secret:name}}` reference is resolved at send time; the workspace `.toml` only ever holds the -*reference*, never the value, so collections stay safe to commit. Where the value comes from is -your choice: - -| Backend | Writable from Posel | Where values live | -|---------|---------------------|-------------------| -| `keychain` *(default)* | yes | macOS Keychain, Windows Credential Manager, Linux Secret Service | -| `file` | yes | a `0600` TOML file in your user data directory | -| `env` | no | the process environment (`{{secret:github_token}}` → `$POSEL_SECRET_GITHUB_TOKEN`) | -| `command` | no | stdout of a helper command you configure (`pass`, `op`, `gh`, …) | -| `none` | — | nowhere; every `{{secret:…}}` fails | - -The keychain is the default and the recommended choice — but not every machine has one. A headless -Linux box, a container, or a CI runner has no Secret Service, which is where -`org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.secrets was not provided by -any .service files` comes from. Pick one of the other backends there. - -Configure it under `[secrets]` in `settings.toml` in your user data directory -(`~/.local/share/posel` on Linux, `~/Library/Application Support/posel` on macOS, -`%LOCALAPPDATA%\posel\data` on Windows): - -```toml -[secrets] -backend = "file" # keychain | file | env | command | none +Secret *values* never enter your workspace — only the `{{secret:name}}` reference does, so the +folder stays safe to commit. Values come from your OS keychain by default; a local file, +environment variables, or a shell command (`pass`, `1Password`, `gh auth token`) all work too. +See [docs/SECRETS.md](./docs/SECRETS.md). -env_prefix = "POSEL_SECRET_" # env backend: name → $POSEL_SECRET_ -command = ["pass", "show", "posel/{name}"] # command backend: stdout is the value -``` +## Why shouldn't I use Posel? -`command` runs the given argv once per lookup and takes its stdout (trailing newline trimmed) as -the value; `{name}` is substituted with the secret name in every argument — e.g. -`["op", "read", "op://vault/{name}/token"]` or `["gh", "auth", "token"]`. A non-zero exit surfaces -the helper's stderr. +- **It's pre-1.0.** It works and it's a usable daily driver, but expect breaking changes. +- **Builds aren't signed yet**, so first launch takes an extra click on macOS and Windows. +- **No Postman/Insomnia collection import yet** — OpenAPI and curl are supported, those aren't. +- **If you want cloud sync or a team workspace server, this isn't it** — and won't be. -**About the `file` backend.** It stores values in plaintext at rest — the same posture as -`~/.aws/credentials`, `~/.docker/config.json`, or `gh`'s `hosts.yml`. On Unix the file is *created* -`0600` (owner-only) before any value is written to it, never chmod'ed afterwards, so the plaintext -is not readable by anyone else even momentarily; on Windows it inherits the user-scoped ACL of -`%LOCALAPPDATA%`. Updates go through a temp file and an atomic rename, entries are namespaced per -workspace, and it lives in your user data directory — **never inside a workspace**, so it can't be -committed by accident. Use the keychain where you have one; use this where you don't. +## Docs -Either way can be overridden for a single run with `POSEL_SECRET_BACKEND`, which wins over -`settings.toml` (an unrecognized value is ignored and the configured backend stands): +- [SPEC.md](./docs/SPEC.md) — what Posel is and the product decisions behind it +- [ARCHITECTURE.md](./docs/ARCHITECTURE.md) — how it's put together +- [SECRETS.md](./docs/SECRETS.md) — secret backends and how to pick one +- [PLAN.md](./docs/PLAN.md) — the phased roadmap +- [CONTRIBUTING.md](./CONTRIBUTING.md) — build it, run the tests, send a PR -```bash -POSEL_SECRET_BACKEND=env cargo run -p posel-app -``` - -`posel-cli` takes the same choice as a flag on `send`, `login`, `test`, and `codegen`: - -```bash -posel-cli send ./ws users/list.toml --secret-backend env # this run only -posel-cli send ./ws users/list.toml --secret token=abc # inline; no store is read -posel-cli send ./ws users/list.toml --keychain # pin the OS keychain -``` - -Given none of those, the CLI resolves through whatever `settings.toml` configures. If several are -given, `--keychain` wins, then `--secret`, then `--secret-backend`. `--secret-backend` swaps only -the backend — the configured `env_prefix` and `command` still apply. - -## A note on the GPUI dependency +## Status -GPUI ships only as a git dependency (no crates.io release), and Posel carries three small, -upstreamable editor enhancements to `gpui-component` (a completion trigger, bracket-aware -auto-indent, and bracket/quote auto-pairing). Those live on a public fork, -[`starvy/gpui-component`](https://github.com/starvy/gpui-component), pinned here to an immutable -tag so the whole graph resolves to a single, reproducible `gpui`. No local checkout is needed — -`cargo build` fetches everything. +Public beta on macOS, Linux, and Windows. Built in Rust on [GPUI](https://www.gpui.rs/), the +framework behind the [Zed](https://zed.dev) editor. ## License -Dual-licensed under either of - -- Apache License, Version 2.0 ([LICENSE-APACHE](./LICENSE-APACHE)) -- MIT license ([LICENSE-MIT](./LICENSE-MIT)) - -at your option. +Dual-licensed under either of [Apache-2.0](./LICENSE-APACHE) or [MIT](./LICENSE-MIT), at your +option. diff --git a/docs/SECRETS.md b/docs/SECRETS.md new file mode 100644 index 0000000..0185c69 --- /dev/null +++ b/docs/SECRETS.md @@ -0,0 +1,62 @@ +# Secrets + +A `{{secret:name}}` reference is resolved at send time; the workspace `.toml` only ever holds the +*reference*, never the value, so collections stay safe to commit. Where the value comes from is +your choice: + +| Backend | Writable from Posel | Where values live | +|---------|---------------------|-------------------| +| `keychain` *(default)* | yes | macOS Keychain, Windows Credential Manager, Linux Secret Service | +| `file` | yes | a `0600` TOML file in your user data directory | +| `env` | no | the process environment (`{{secret:github_token}}` → `$POSEL_SECRET_GITHUB_TOKEN`) | +| `command` | no | stdout of a helper command you configure (`pass`, `op`, `gh`, …) | +| `none` | — | nowhere; every `{{secret:…}}` fails | + +The keychain is the default and the recommended choice — but not every machine has one. A headless +Linux box, a container, or a CI runner has no Secret Service, which is where +`org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.secrets was not provided by +any .service files` comes from. Pick one of the other backends there. + +Configure it under `[secrets]` in `settings.toml` in your user data directory +(`~/.local/share/posel` on Linux, `~/Library/Application Support/posel` on macOS, +`%LOCALAPPDATA%\posel\data` on Windows): + +```toml +[secrets] +backend = "file" # keychain | file | env | command | none + +env_prefix = "POSEL_SECRET_" # env backend: name → $POSEL_SECRET_ +command = ["pass", "show", "posel/{name}"] # command backend: stdout is the value +``` + +`command` runs the given argv once per lookup and takes its stdout (trailing newline trimmed) as +the value; `{name}` is substituted with the secret name in every argument — e.g. +`["op", "read", "op://vault/{name}/token"]` or `["gh", "auth", "token"]`. A non-zero exit surfaces +the helper's stderr. + +**About the `file` backend.** It stores values in plaintext at rest — the same posture as +`~/.aws/credentials`, `~/.docker/config.json`, or `gh`'s `hosts.yml`. On Unix the file is *created* +`0600` (owner-only) before any value is written to it, never chmod'ed afterwards, so the plaintext +is not readable by anyone else even momentarily; on Windows it inherits the user-scoped ACL of +`%LOCALAPPDATA%`. Updates go through a temp file and an atomic rename, entries are namespaced per +workspace, and it lives in your user data directory — **never inside a workspace**, so it can't be +committed by accident. Use the keychain where you have one; use this where you don't. + +Any of these can be overridden for a single run with `POSEL_SECRET_BACKEND`, which wins over +`settings.toml` (an unrecognized value is ignored and the configured backend stands): + +```bash +POSEL_SECRET_BACKEND=env posel +``` + +`posel-cli` takes the same choice as a flag on `send`, `login`, `test`, and `codegen`: + +```bash +posel-cli send ./ws users/list.toml --secret-backend env # this run only +posel-cli send ./ws users/list.toml --secret token=abc # inline; no store is read +posel-cli send ./ws users/list.toml --keychain # pin the OS keychain +``` + +Given none of those, the CLI resolves through whatever `settings.toml` configures. If several are +given, `--keychain` wins, then `--secret`, then `--secret-backend`. `--secret-backend` swaps only +the backend — the configured `env_prefix` and `command` still apply.