Contributing
Architecture
The layer table, the rules that hold across crates, the workspace on disk and the measured design decisions.
Layer table (enforced by cargo xtask layers)
A crate may depend only on lower layers.
| Layer | Crate | Role |
|---|---|---|
| L0 | postguy-model | Plain data and serde: ids, collections, folders, requests (HTTP/gRPC/WS), key-value rows, bodies, auth configs, settings, environments, examples, history. No I/O. Unknown fields are preserved in extras. |
| L0 | postguy-util | Size-capped readers, depth-limited JSON walker, ring buffer, fractional ordering, atomic file helpers, time (local-time formatting through jiff; the offset falls back to UTC when the system zone is unreadable). |
| L1 | postguy-vars | Variable scopes and precedence, {{var}} resolver (depth limit, cycle guard), dynamic variables, secret masking, {{% %}} body shortcodes (shortcode). Pure. |
| L1 | postguy-urlkit | URL parse/build, per-component percent-encoding, query/URL two-way sync, path variables, host matching. Pure. |
| L2 | postguy-auth | Pure signing and credential algorithms. A canonical request goes in; headers and params come out. No sockets. |
| L2 | postguy-interop | Import/export (Postman v2.0/v2.1, environments, cURL, OpenAPI, HAR, Bruno folders) with ImportReport. Bounded sizes and nesting. |
| L2 | postguy-store | Workspace on disk: atomic writes with .bak, lock file, trash, history, session, settings, secrets split. |
| L3 | postguy-http | hyper-direct connector: connect/TLS/timing, redirects, compression, cookie jar, proxy, auth retries, streaming, cancellation. |
| L3 | postguy-grpc | protox compile, descriptor pool, reflection, dynamic invoke, examples, status/trailers. |
| L3 | postguy-ws | tokio-tungstenite WebSocket client: handshake, TLS, frames, reconnect policy, message log. |
| L3 | postguy-script | rquickjs (QuickJS) sandbox with the pm API, its superset pg (plus Bruno's bru/req/res/test/expect), request assertions, asset-backed require with read-only fs/path/Buffer/crypto.randomUUID shims, Chai-style pm.expect and hard wall-clock, memory, stack and cancel limits. All of it is Rust closures. Settings.disable_scripts is the off switch. |
| L3 | postguy-snippets | Code generation (cURL, fetch, requests, Go, reqwest, PowerShell, axios, grpcurl). Pure. |
| L3 | postguy-testkit | Loopback servers for tests only. A dev-dependency, never shipped. |
| L4 | postguy-runner | Collection runs: plans, iterations, CSV/JSON data files, setNextRequest sequencing, results model, CLI/JSON/JUnit/HTML/CSV/XLSX/PDF reporters (one ReportTable feeds the CSV, XLSX and PDF writers). Pure. |
| L6 | postguy-engine | Façade and session: workspace handle, edits/undo, send pipeline (12 steps), per-collection assets, Bruno folder import, runtime variables, command registry, console, history, runtime. |
| L7 | postguy-platform | OS services. Today: open_url, save_export, pick_data_file and pick_import_paths (crates/platform/src/lib.rs). Planned: directories, reveal, clipboard, system proxy and system font fallback. |
| L7 | postguy-ui-egui | The egui shell (eframe + glow): widgets and screens. Depends on postguy-vars (shortcode highlighting) and postguy-util (times). |
| L7 | postguy-automation | Headless tool table over the engine, CLI backend, opt-in UI control channel. The opt-in MCP server is planned, not shipped. |
| apps | postguy, postguy-cli | Desktop app (with the opt-in --control server in apps/postguy/src/control.rs). Headless CLI with the verbs tools, call, run (--script / --collection), ui and the dev-only fuzz-target (apps/postguy-cli/src/main.rs). Import, export and snippets go through the call / run --script tools. postguy-cli mcp does not exist yet. |
Tooling: xtask (build system, gates, fuzz, icons/logo, size) is outside the layer system.
Rules that hold everywhere
- Never crash (AGENTS.md 4):
- Use
Resulteverywhere. Nounwraporpanicin non-test code. unsafe_code = "forbid".- Use checked arithmetic on input-sized values.
- Bound recursion and allocations.
- Use
catch_unwindat import, script and command entry points. panic = "unwind".
- Use
- Layering: a crate may depend only on lower layers. Nothing below L7 reaches egui, winit, eframe or rfd. The layers gate walks the normal-dependency closure over
cargo metadata. - Concurrency:
engineowns one tokio runtime on dedicated threads.- The UI receives events and repaints once per batch.
- Every long operation takes a cancellation token and wraps panics into error rows.
- Workspace on disk: one folder.
- Contents:
postguy.json,collections/<id>.json,environments/,globals.json,protos/,files/,assets/<collection id>/,history.jsonl,.trash/,.postguy/{secrets,cookies,responses,runs,lock}. - Writes are atomic. There is a
.bakfile and corrupt-file recovery. - Settings, session and the recent-workspaces list live in
.postguy-config/{settings,session,recent}.jsoninside the workspace (apps/postguy/src/main.rs).
- Contents:
Decisions made with measurements
- Renderer: eframe glow renderer. It is smaller and warms up faster than wgpu.
- Response bodies: bytes, plus an off-thread line index, plus
show_rowsvirtualisation. - HTTP stack: hyper-direct connector. It gives control of timings, redirects, header order and cancellation. tonic shares the same hyper stack.
- Scripting: rquickjs (QuickJS, MIT). The owner approved it for its memory limit, interrupt handler and stack limit.
- boa_engine exposes none of these and is not used.
rust-allocstays off because it turnsset_memory_limitinto a no-op.- Windows x86 and FreeBSD build the bindings with
bindgen, because no pregenerated ones ship for those targets.
- Icons and logo: build-time path data plus painter strokes. No SVG renderer is shipped.