# wip-fs-server `wip-fs-server` projects one configured Linux filesystem directory onto WIP Worldspace `/` at request time. It does not register the filesystem contents or keep one object instance per entry. It uses `wip-host` for publication and typed operation dispatch and uses the standard `wip-http` routes, JSON codecs, envelopes, and HTTP status mapping. ## Security model - The effective root is opened once as a directory capability. Root path components must be real directories (symlink components are rejected), and the displayed effective root is read back from that same pinned descriptor. Every request is resolved segment-by-segment with directory-relative, `O_NOFOLLOW` handles. - Symlinks are projected as links and are never followed. Their stored target is available only through the `SymlinkInspect.read_link` application operation. - Regular files, directories, symlinks, and unsupported entry types have distinct public descriptions and capability sets. - WIP names are Unicode. Linux names that are not valid UTF-8 are deliberately not published; valid UTF-8 names map byte-for-byte and therefore cannot collide. Unicode paths containing backend-invalid NUL bytes are likewise treated as unpublished rather than as backend failures. - No object `ref` is issued. This avoids claiming representation identity for hard links or path-specific names. Regular files and symlinks receive an opaque validator containing filesystem identity plus protected metadata; replacing a path therefore does not preserve its validator. - Protocol metadata and errors never contain the backend absolute path. Startup prints only the explicitly configured effective root and security-sensitive modes. - The built-in HTTP listener is loopback-only. TLS termination, certificates, authentication, an externally visible URL scheme, and proxy routing are deployment concerns rather than separate WIP transport profiles. The implementation currently targets Linux because it uses `O_PATH`, directory-relative descriptor traversal, and `/proc/self/fd` to reopen the same pinned backend object without resolving the untrusted Worldspace path twice. ## Run Run the loopback listener in read-only mode: ```console cargo run -p wip-fs-server -- \ --root /srv/public-files \ --listen 127.0.0.1:8080 \ --base-path /wip ``` Run with the write capability deliberately enabled: ```console cargo run -p wip-fs-server -- \ --root ./example-files \ --listen 127.0.0.1:8080 \ --base-path /wip \ --read-write ``` The server reports the effective root, `ReadOnly`/`ReadWrite` mode, endpoint, and actual listen address. It does not enumerate the root. ## Standard interactions The following examples use the built-in HTTP listener. The JSON follows the standard WIP over HTTP binding; validators and operation `Bytes` are base64 body fields, never `ETag` or `If-Match` headers. Observe only the root Object: ```console curl -sS -X POST http://127.0.0.1:8080/wip/v1/observe \ -H 'Content-Type: application/json' -H 'Accept: application/json' \ --data '{"path":"/","depth":0}' ``` Observe the root and two levels of indexable children: ```console curl -sS -X POST http://127.0.0.1:8080/wip/v1/observe \ -H 'Content-Type: application/json' -H 'Accept: application/json' \ --data '{"path":"/","depth":2}' ``` A regular-file observation returns the opaque read interface reference and an object validator. Retrieve that descriptor with: ```console curl -sS -X POST http://127.0.0.1:8080/wip/v1/fetch_interface \ -H 'Content-Type: application/json' -H 'Accept: application/json' \ --data '{"interface":""}' ``` Then call a bounded range read: ```console curl -sS -X POST http://127.0.0.1:8080/wip/v1/call_operation \ -H 'Content-Type: application/json' -H 'Accept: application/json' \ --data '{ "target":{"path":"/notes.txt","validator":""}, "interface":{"reference":""}, "operation":"read", "arguments":{"offset":0,"length":4096} }' ``` In `--read-write` mode a second regular-file interface declares `replace`. Writes require the current object validator and return the post-write validator: ```console curl -sS -X POST http://127.0.0.1:8080/wip/v1/call_operation \ -H 'Content-Type: application/json' -H 'Accept: application/json' \ --data '{ "target":{"path":"/notes.txt","validator":""}, "interface":{"reference":""}, "operation":"replace", "arguments":{"contents":"dXBkYXRlZA=="} }' ``` ## Resource and failure behavior `ResourceLimits` bounds request/response bytes, HTTP request-head bytes, JSON nesting, file read/write bytes, tree depth/node count, pinned handles, and active connections/requests. One absolute request deadline covers header and body intake, dispatch, cooperative filesystem work, and response transmission; stalled clients are closed and cannot occupy a worker beyond that bound. Workers accept connections directly, so the listener never moves more than the configured concurrency into active userspace service. Advisory file-lock acquisition uses a nonblocking loop and checks the same deadline/cancellation rather than allowing a contended lock to stall a worker. Object observation recursively opens only the requested depth. A successful response is complete through that depth; if the configured projected-node budget would be exceeded, the interaction fails with `ResourceLimitExceeded` instead of returning a partial observation. I/O failure and cancellation remain errors. Whole-file replacement compares the request validator against the identity and state of the same pinned file handle used for the write. Failures before mutation carry a `NotCommitted` guarantee and map to `Internal` or `ResourceLimitExceeded`; a failure after truncation/write begins maps to `OperationOutcomeUnknown`. The server and Clients do not automatically retry side-effecting operations.