2026-08-31 21:01:50 +09:00
2026-08-31 21:01:50 +09:00
2026-08-31 21:01:50 +09:00
2026-08-31 21:01:50 +09:00

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 HTTPS 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

wip-tui (interactive Worldspace explorer and operation editor)
├── wip-client                Known Space, request identity, validators, and calls
├── wip-https                 framework-neutral HTTP request/response messages
└── wip-protocol              descriptors and logical values

wip-fs-server (request-time filesystem projection and loopback listener)
├── wip-https                 standard routes/codecs/envelopes/batching
├── wip-host                  request-scoped publication and dispatch
└── wip-interface             macro-generated filesystem capabilities

wip-public-api-example (workspace-only executable, integration tests, doctest)
├── wip-protocol              logical declarations, values, calls, and validation
├── wip-https                 standard HTTPS v1 routes, JSON codecs, envelopes, and batching
│   └── wip-protocol
├── wip-client                stateful Known Space, lazy tree, observation, and call runtime
│   ├── wip-https
│   └── wip-protocol
├── wip-interface             application DTO codecs and interface authoring facade
│   ├── wip-protocol
│   └── wip-interface-macros  proc-macro implementation detail
└── wip-host                  descriptor registry, publication, and qualified dispatch
    ├── wip-interface
    └── wip-protocol
Crate Responsibility Does not own
wip-protocol Transport-independent descriptors, object/call/result types, logical Values, validation, and stable protocol error categories. Application DTOs, implementations, persistence, or wire encoding.
wip-https Standard HTTPS v1 endpoint/route derivation, UTF-8 JSON codecs, direct success bodies, error/status classification, and retrieval batches. HTTP I/O, TLS termination, authentication schemes, server frameworks, filesystem projection, or client presentation/cache policy.
wip-client Endpoint/security-context-isolated Known Space, normalized object and interface observations, lazy entry trees, 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 HTTPS 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, bounded BFS trees and file operations, standard wip-https dispatch/batch handling, and a loopback HTTP binary for TLS-proxy or explicit development use. A new wire profile, implicit symlink following, non-loopback cleartext, authentication policy, 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.

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-https maps standard routes/envelopes to CallOperationRequest and CallOperationResponse without placing transport-specific types in descriptors or application traits.

End-to-end example

crates/wip-public-api-example/src/lib.rs is the complete public-API example and 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 opaque 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:

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

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<bool>;
}

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<T> return;
  • () only as the application return when no value is produced; and
  • direct Option<T> 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<T> 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<T> 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<K, V> 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:

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 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. HTTPS is the default; loopback plain HTTP requires --allow-http. Full startup, TLS, key binding, layout, validator, and filesystem-operation examples are in the package README.

Filesystem projection 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, performs bounded breadth-first tree discovery, and publishes validator-protected replacement only in explicit read-write mode. The built-in cleartext listener is loopback-only: production HTTPS is explicitly delegated to a reverse proxy, while local plain HTTP requires an opt-in flag.

Validation

Enter the dev shell and run the acceptance commands exactly:

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.

S
Description
No description provided
Readme
1 MiB
Languages
Rust 100%