# wip-rs Rust building blocks for the Web Interface Protocol (WIP). The repository is a resolver-3 virtual Cargo workspace whose packages all live under `crates/*`. It provides a transport-independent protocol model, the standard framework-independent WIP over HTTP v1 binding, a stateful presentation-independent client runtime, a Rust interface-authoring facade, a request-scoped host runtime, a bounded Linux filesystem projection server, an interactive terminal client, and a private public-API conformance consumer. The repository root is not itself a package. ## Crate graph and responsibilities ```text Published libraries wip-client ├── wip-http │ └── wip-protocol └── wip-protocol wip-host ├── wip-interface │ ├── wip-interface-macros │ └── wip-protocol └── wip-protocol Workspace-only applications and conformance consumers wip-tui ├── wip-client ├── wip-http └── wip-protocol wip-fs-server ├── wip-host ├── wip-http ├── wip-interface └── wip-protocol wip-public-api-example ├── wip-host ├── wip-interface └── wip-protocol ``` | Crate | Responsibility | Does not own | | --- | --- | --- | | `wip-protocol` | Transport-independent descriptors, object/call/result types, logical `Value`s, validation, and stable protocol error categories. | Application DTOs, implementations, persistence, or wire encoding. | | `wip-http` | Standard WIP over HTTP v1 endpoint/route derivation, UTF-8 JSON codecs, direct success bodies, and error/status classification. | HTTP I/O, URL scheme or TLS policy, authentication schemes, server frameworks, filesystem projection, or client presentation/cache policy. | | `wip-client` | Endpoint/security-context-isolated Known Space, normalized object and interface observations, intent-driven object and descriptor coverage with bounded request deduplication, immutable operation call contexts, stale transitions, and bounded histories. | HTTP I/O, credentials, persistent cache, automatic operation retries, or presentation widgets/events. | | `wip-tui` | Keyboard-only interactive Worldspace tree, selected Entry and qualified operation panes, descriptor-driven argument editing, bounded result/diagnostic rendering, terminal lifecycle, and HTTP I/O workers over `wip-client`. | Independent cache/validator semantics, automatic operation retries, Host internals, backend paths, or persistent state. | | `wip-interface` | The public `WipType`/`interface` authoring API, Rust-to-protocol codecs, generated descriptors, and typed implementation dispatch. | Object publication, backend lifetime, or transport policy. | | `wip-interface-macros` | Procedural-macro expansion used by `wip-interface`. It is not an application dependency. | A supported direct user API. | | `wip-host` | Opaque interface registration, explicit object membership, validator checks, interface-qualified dispatch against one host-owned instance, and descriptor-guided argument decoding inside that same resolution boundary. | Backend atomicity, global object lookup, or network serving. | | `wip-fs-server` | Linux directory-handle-confined request-time projection, complete depth-bounded observations and bounded file operations, standard `wip-http` dispatch, and a loopback HTTP binary. | A new wire profile, implicit symlink following, non-loopback cleartext, authentication policy, TLS termination, or filesystem-wide preregistration. | | `wip-public-api-example` | An independently compiled consumer of the protocol, authoring, and host public crates. Its DTOs and traits are application-owned; its implementation and backend handle remain host-private. | Reusable library API or a production server. | ## Published crates The `0.1.0` release consists of these six crates: - `wip-protocol` - `wip-interface-macros` — implementation dependency re-exported by `wip-interface` - `wip-interface` - `wip-host` - `wip-http` - `wip-client` The filesystem server, terminal client, and public-API example are workspace-only packages and set `publish = false`. All workspace packages require Rust 1.88 or newer and are licensed under either Apache-2.0 or MIT at your option. See [RELEASING.md](RELEASING.md) for the dependency-ordered crates.io procedure and [CHANGELOG.md](CHANGELOG.md) for release contents. The boundary is intentional: protocol types describe what can cross a transport; application DTOs opt into that model with `WipType`; a host-private implementation owns credentials, handles, and backend state; and `wip-http` maps standard routes/envelopes to `CallOperationRequest` and `CallOperationResponse` without placing transport-specific types in descriptors or application traits. ## Structured Interface references The unreleased Protocol/HTTP API uses explicit `InterfaceReference { scope, name }` records and optional `scope_ref` observation/precondition metadata. Legacy opaque-string wire references are rejected. See [MIGRATION.md](MIGRATION.md) for construction examples, validation boundaries and downstream migration status. ## End-to-end example [`crates/wip-public-api-example/src/lib.rs`](crates/wip-public-api-example/src/lib.rs) is the complete public-API example and [`crates/wip-public-api-example/src/main.rs`](crates/wip-public-api-example/src/main.rs) runs it. It demonstrates all of the following: 1. public application DTOs derived as `WipType` records, enums, and unions, including canonical entry paths and schema-opaque JSON; 2. two public traits declared with `#[wip::interface]`; 3. a private host struct implementing both traits; 4. explicit publication of both interfaces as an ordered set on one object; 5. separate observation of structured interface references, descriptors, and descriptor validators; 6. calls qualified by `target.path`, object validator, interface reference and validator, operation, and schema-neutral `Value` arguments; 7. validated result values decoded back to the typed application result; 8. independent dispatch of the same `status` operation name on both interfaces; 9. distinct domain rejection, rollback-confirmed and outcome-unknown host/backend failures, `PermissionDenied`, stale validator recovery, interface membership, and lookup failure codes; and 10. canonical failure precedence when several request fields are invalid. Run it inside the development shell: ```console nix develop cargo run --package wip-public-api-example ``` The public-only conformance suite is in [`crates/wip-public-api-example/tests/end_to_end.rs`](crates/wip-public-api-example/tests/end_to_end.rs). It covers canonical object/descriptor shape, ordered interfaces, schema-neutral record/enum/union/entry/JSON values, fully qualified calls, and the distinct domain, host, authorization, validator-recovery, and interface failure classes. Exhaustive public type construction and matching make legacy ObjectId, descriptor `name`, and semantic `Value` variants compile-time regressions. Unit tests in the public crates additionally cover every host dispatch failure, `InvalidRequest` versus descriptor-bound `InvalidArguments`, invalid local responses, validators, same-named operations, and exhaustive per-interaction protocol error-code sets. ## Interface authoring Applications depend on `wip-interface`, normally imported as `wip`: ```rust use wip_interface as wip; #[derive(wip::WipType)] struct Message { /// Message body. text: String, } #[wip::interface] pub trait Messenger { /// Sends a message. /// /// # Returns /// Whether the application accepted the message. fn send( &self, /// Message to send. message: Message, ) -> wip::OperationResult; } ``` The `interface` attribute and `WipType` derive are re-exported by the facade; users do not import `wip-interface-macros` or generated private support symbols. The generated `MessengerDefinition` constructs an `InterfaceDescriptor`, decodes named arguments, encodes results, validates both boundaries, and provides typed dispatch for any `T: Messenger`. ### Supported Rust syntax A supported interface is a public, non-generic trait containing methods with: - exactly an immutable `&self` receiver; - simple named, owned parameters; - an explicit `wip::OperationResult` return; - `()` only as the application return when no value is produced; and - direct `Option` only for an omittable parameter. `WipType` derives support named-field structs, all-unit enums, and unions whose cases may be unit variants or variants containing exactly one unnamed payload. The macros reject private, generic, inherited, unsafe, auto, or associated-item traits; const, async, unsafe, extern, variadic, generic, default-bodied, or non-`&self` methods; borrowed/custom-lifetime values; tuple/unit/generic structs; Rust unions; named-field/multi-field enum variants; explicit discriminants; nested `Option`; and `Option` in returns, collections, entries, or union payloads. These diagnostics are compile-fail tested under `crates/wip-interface/tests/ui`. ### Rust type mapping | Rust | Protocol | | --- | --- | | `()` | unit / `Value::Unit` | | `bool` | boolean | | integer primitives | JavaScript-lossless integer | | `f32`, `f64` | finite number | | `String` | string | | `wip::Bytes` | bytes | | `wip::JsonValue` | JSON interpreted over the schema-neutral `Value` tree | | `wip::Entry` | canonical absolute Worldspace path encoded as a string | | `Vec` | list | | named-field `#[derive(wip::WipType)]` struct | named record | | all-unit `#[derive(wip::WipType)]` enum | named enum encoded as a string | | mixed unit/newtype or all-newtype enum | named union encoded as a discriminator record | `Option` means that a direct DTO field or operation parameter may be omitted; it is not protocol null. JSON null remains available inside `wip::JsonValue`. #### Migration from the pre-canonical value API The old generic `wip::Entry` represented an application key/value pair and is no longer available. Use `wip::Entry::new("/canonical/path")` for a Worldspace entry path and place any associated application data in a record. `Entry` encodes as `Value::String` and validates the canonical absolute path. The semantic `Value::Json`, `Value::Entry`, `Value::Enum`, and `Value::Union` variants have also been removed. JSON recursively uses `Unit`, primitive, `List`, and `Record` values and rejects `Bytes`; enums use `String`; unions use a `Record` containing `$case` and, for payload-bearing cases, `value`. Unit operation results are represented explicitly as `Value::Unit`. Rust doc comments become descriptor metadata. For an operation, a `# Returns` section documents the return declaration; text before that section documents the operation itself. ## Host publication and dispatch Applications use `wip-host` to register generated definitions and explicitly choose the ordered interface set for each object publication: ```rust,ignore let mut interfaces = wip_host::InterfaceRegistry::new(); let messenger = interfaces.register(MessengerDefinition, None)?; let published = wip_host::PublishedObject::new(request_scoped_object) .expose(&messenger)?; let protocol_object = published.protocol_object(); ``` `expose` compiles only when the object implements the generated definition's interface trait. Implementing a trait does not expose it automatically, empty interface sets are valid, insertion order is preserved, and duplicate references are rejected. Registry references are opaque and remain outside the descriptor; generated descriptors contain neither those references nor Rust trait identity. Dispatch is qualified by interface reference and operation name and targets a canonical Worldspace path. An `ObjectProvider` validates that path before resolution, maps malformed paths to `InvalidRequest`, then resolves a valid path exactly once inside `with_object`; the host checks the supplied object validator, current interface membership, interface validator, descriptor operation, operation-specific object-validator policy, arguments, and authorization in canonical precedence. Path and children remain provider resolution context and are never projected by `WipObject`; host-private fields and backend handles are also excluded. Application domain failures belong in a derived result union and are returned through `Ok`. `wip::HostError` is reserved for runtime/backend failures. A structurally malformed call is `InvalidRequest`; only argument values checked after the current Interface Descriptor is fixed are `InvalidArguments`. A Host must never return a wire `InvalidResult`: Clients keep invalid Object, Descriptor, operation-result, and error responses as local `InvalidResponse` failures, while unsupported descriptor formats remain local `UnsupportedDescriptorFormat` failures. For operation calls, `Internal` and `ResourceLimitExceeded` guarantee that the operation did not run or that its effects were rolled back. `OperationOutcomeUnknown` is the only protocol code that makes no such guarantee, so Clients must not automatically retry it. `wip::HostError` therefore requires both a `HostErrorKind` and an `EffectGuarantee`; implementation panic, result encoding, and result validation default to an unknown outcome. Host-private diagnostics are bounded locally but never copied into protocol messages. `ExecutionContext` carries a runtime-neutral cancellation token, monotonic deadline, and optional Host-defined resource budget through resolution and execution. Cancellation and deadlines are cooperative: the runtime checks them immediately before typed invocation, while providers enforce them during backend work. A pre-invocation limit failure is `ResourceLimitExceeded`; after invocation it is safe to use that code only when non-commit or rollback is confirmed. Providers remain responsible for backend atomicity. `with_object` returns a typed `ProviderResult`, and `execute_operation` receives an `OperationAttempt` that separates `NotStarted` from `Started`. A transactional provider holds its transaction across the callback, commits successful attempts, and may call `OperationAttempt::confirm_rollback` only after every operation effect has been rolled back. Without that proof, post-invocation failures remain `OperationOutcomeUnknown`. Mutable operations must still make validator comparison and backend commit atomic and return a validator describing the state that becomes committed. ## Interactive terminal client [`crates/wip-tui`](crates/wip-tui) provides the `wip-tui` executable. It starts from Worldspace `/`, lazily expands materialized tree nodes, displays current object/interface/operation metadata, validates all canonical descriptor types in a field editor, and requires a separate confirmation action before every call. It uses the stateful `wip-client` runtime for request correlation, observed validators, stale transitions, and unknown outcomes. One canonical `http` or `https` endpoint is used unchanged for request construction, transport, and cache partitioning. Scheme, TLS, certificates, authentication, and proxy policy belong to the HTTP implementation and deployment. Full startup, key binding, layout, validator, and filesystem-operation examples are in the [package README](crates/wip-tui/README.md). ## Filesystem projection server [`crates/wip-fs-server`](crates/wip-fs-server) contains the practical server binary and its full security model, configuration, curl walkthrough, and resource/failure semantics. It resolves every object from current filesystem state, uses pinned directory-relative handles without following symlinks, omits non-UTF-8 backend names, returns complete recursive observations through the requested depth or a resource-limit failure, and publishes validator-protected replacement only in explicit read-write mode. The built-in HTTP listener is loopback-only. Externally visible URL schemes, TLS termination, certificates, authentication, and proxy routing are deployment concerns rather than separate WIP profiles. ## Validation Enter the dev shell and run the acceptance commands exactly: ```console nix develop cargo fmt --all -- --check cargo check --workspace --all-targets cargo test --workspace --all-targets cargo test --workspace --doc cargo clippy --workspace --all-targets -- -D warnings RUSTDOCFLAGS='-D warnings' cargo doc --workspace --no-deps cargo run --package wip-public-api-example git diff --check ``` `cargo test --workspace --all-targets` includes the public-only conformance consumer, runtime failure tests, and `trybuild` compile-fail suites. The separate doctest, rustdoc, executable, and whitespace checks are all required acceptance steps.