Web File Server and Web File Manager

The Web File Server (WFS) exposes one Barracuda I/O interface through three complementary interfaces:

The server implementation is the Lua module wfs.lua. The browser client is an ES module in /rtl/wfm/wfm.js, with default styles in /rtl/wfm/wfm.css. The JSON service and WebDAV server operate on the same I/O object, so changes made through one interface are immediately visible through the others.

Security: A writable file server is a sensitive application surface. Install an authenticator, and normally an authorizer, before exposing it on a network. A session URL is a bearer credential and must be protected in the same way as a password.

Loading the module

local wfs = require"wfs"

Loading the module performs two actions:

  1. It installs the backward-compatible function ba.create.wfs.
  2. It returns a table containing wfs.create and wfs.wfm.

Construction modes

ConstructionCommandless directory GETTypical use
ba.create.wfs(...) Returns the standard full-page WFM. A ready-to-use standalone file manager, JSON service, and WebDAV server at one mount point.
wfs.create(...) Returns HTTP 400 JSON with {"err":"missingcmd"}. A JSON/WebDAV service used by a WFM embedded in another page.
wfs.create(..., wfs.wfm(title)) Returns the standard full-page WFM. An explicitly constructed standalone manager with a custom page title.
wfs.create(..., pagefunc) Calls the supplied function. A custom page shell that owns layout, styling, and plugin assembly.

All four forms expose the same JSON/HTTP and WebDAV operations. The only difference is how a directory GET without a cmd argument is handled.

Server constructors

ba.create.wfs([name] [,priority], io [,lockdir] [,maxuploads, maxlocks])

Creates a ready-to-use standalone WFS. Its public interface supplies a lazily created and shared standard WFM page callback. Use wfs.create when a different page callback is required.

Parameters

Return values

Throws

Throws when the parsed io is not a BAS I/O interface, supplied numeric values cannot be converted to integers by the native constructors, maxlocks is missing after maxuploads, maxuploads exceeds the native upload range, or Lua allocation fails. Optional arguments are recognized by type; omit unused ones. There is no constructor nil/error return for later filesystem or request failures.

wfs.create([name] [,priority], io [,lockdir] [,maxuploads, maxlocks] [,pagefunc])

Creates a WFS without adding the standard page callback. Supply wfs.wfm() or your own pagefunc when direct browser navigation to WFS directory URLs should produce a page.

Parameters

Return values

Throws

Throws when the parsed io is not a BAS I/O interface, supplied numeric values cannot be converted to integers by the native constructors, maxlocks is missing after maxuploads, maxuploads exceeds the native upload range, or Lua allocation fails. Optional arguments are recognized by type; omit unused ones. There is no constructor nil/error return for later filesystem or request failures.

Shared arguments

Optional arguments are recognized by type. Omit unused arguments instead of inserting nil placeholders. Both constructors return a WFS table. See the constructor reference for numeric conversion, return, and error details.

name
Optional string VFS directory name, such as "fs" for a server mounted at /fs/.
priority
Optional integer VFS search priority, -127 through 127. The default is 0.
io
The required BAS I/O userdata containing the files. Create it with ba.openio or ba.mkio. The I/O object must be writable to support uploads, renames, and deletion.
lockdir
Optional string directory path used by the underlying WebDAV lock manager. Create it before mounting the WFS. A dot-prefixed name such as .LOCK keeps it out of ordinary directory listings.
maxuploads
Optional integer limit for concurrent uploads, default 5. The WebDAV and browser/HTTP uploaders each enforce this limit independently; for example, 5 allows up to five uploads of each kind concurrently. It is not a file-size limit or a combined limit across both handlers. Values below 1 become 1; values above the maximum C int throw.
maxlocks
Integer maximum number of WebDAV locks. The default is 20. When specifying limits, provide both numbers.

wfs.create page callback

The optional pagefunc argument belongs to wfs.create, not ba.create.wfs. It is called for a commandless directory GET and receives the request command environment and the path relative to the WFS: pagefunc(_ENV, relpath). WFS authentication and authorization run before this callback. The callback is not used for files, JSON commands, uploads, or WebDAV methods.

Parameters

Return values

Throws

Errors propagate through WFS to the native directory callback error handler; they are not constructor errors. Response operations inside the callback retain their own return/throw behavior.

Standalone example

local wfs = require"wfs"
local io = ba.openio"home"
local lockdir = ".LOCK"

if not io:stat(lockdir) then
   assert(io:mkdir(lockdir))
end

-- ba.create.wfs includes the ready-to-use full-page WFM.
app.files = ba.create.wfs("fs", io, lockdir)
app.files:insert()

Opening /fs/ now starts the WFM. Opening a directory URL such as /fs/documents/ starts the manager in that directory. Directory URLs are canonicalized with a trailing slash.

Custom full-page callback

local wfs = require"wfs"
local io = ba.openio"home"

app.files = wfs.create(
   "fs", io, ".LOCK", wfs.wfm"Device Files"
)
app.files:insert()

wfs.wfm([title]) returns the standard full-page callback. The default title is WFM. A completely custom callback can be supplied instead when an application needs a different page shell.

wfs.wfm([title])

Parameters

Return values

Throws

Throws for an unsupported title value or errors during LSP generation/allocation. Page execution happens later and can raise response or runtime errors through the directory callback handler.

WFS object

The returned object inherits the standard directory methods, including insert, unlink, baseuri, and header configuration. Keep the object referenced for as long as it is mounted.

wfs:setauth(authenticator [,authorizer])

Installs authentication and optional authorization on both the JSON/HTTP service and the WebDAV service. Authorization uses the corresponding HTTP or WebDAV operation, such as GET, PUT, DELETE, MKCOL, and PROPFIND.

Parameters

Return values

Throws

Throws for an invalid WFS object, an authenticator/authorizer with the wrong native interface, or allocation failure. Reinstalls the request service callback; the configured filterservice is preserved.

app.files:setauth(authenticator, authorizer)
app.files:configure{tmo=15*60}

wfs:configure(options)

Replaces optional WFS behavior. The recognized fields are:

Parameters

tmo
Optional integer maximum idle time, in seconds, for Session URLs. A nonzero timeout and an installed authenticator or authorizer enable Copy Session URL in the WFM. Set tmo to zero or omit it to disable Session URLs.
filterservice
Optional function or false; nil and omission also disable it. An advanced relative-path filter. The callback receives (_ENV, relpath [,session]) and must return the relative path that WFS should serve. The optional session value is supplied when a Session URL resolves to a session.
pageaccessdenied
An optional function callback for presenting an authorization failure when a commandless browser GET would otherwise open the configured WFM page. The callback receives (_ENV, relpath, method) after WFS sets status 403 and must write the response. It is not used for JSON commands, file operations, uploads, or WebDAV methods, which retain their protocol-specific error responses.
app.files:configure {
   tmo = 15 * 60,
   pageaccessdenied = function(env, relpath, method)
      env.response:setcontenttype "text/html; charset=utf-8"
      env.response:write "<h1>File access unavailable</h1>"
   end
}

Return values

Throws

Throws for an invalid WFS object/options argument, a truthy non-function filterservice, or a non-nil non-function pageaccessdenied. Table access and allocation errors can also throw. Callback validation occurs before changing the configuration. The timeout is stored without validation here; invalid timeout values may fail later when a Session URL is used.

filterservice(_ENV, relpath [,session])

The filter callback has the following contract:

Parameters

Return values

Throws

Callback errors propagate through WFS request processing to the directory callback error handler. WFS does not convert them into a configure return value. Returning an invalid path may fail during subsequent request processing.

Each configure call replaces the configuration. A supplied filterservice replaces the previous filter; omission, nil, or false removes it. Calling setauth() preserves the configured filter while updating authentication and Session URL routing. A filter runs once per request. When a Session URL is resolved, the filter receives the resolved relative path and session; changing the path does not discard the session.

pageaccessdenied(_ENV, relpath, method)

Parameters

Return values

Throws

Callback errors propagate to the directory callback handler. WFS stops request processing after a normal return; this callback does not grant access.

configuration.authorize(_ENV, relpath, method [,mode])

Parameters

Return values

Throws

On denial, writes a 403 response and aborts the request. Authorizer, response, and callback errors can propagate. The returned function is a snapshot: obtain a new helper after changing authorization with setauth.

wfs:service(request, relpath)

Delegates a request directly to the WFS resource reader. Normal applications mount the WFS in the virtual file system and do not call this method directly.

Parameters

Return values

Throws

Throws for an invalid WFS or request object, an expired request, or an invalid relpath argument. Native service dispatch retains its own callback error handling. This wrapper does not return a separate nil/error result.

Session URLs

Session URLs are intended for simple WebDAV or HTTP clients that cannot perform normal authentication. When enabled, cmd=sesuri returns a URL containing a public session reference. WFS resolves that reference with ba.session and applies the configured idle timeout.

If Session URLs are unavailable, the WFM disables Copy Session URL. For an unauthenticated WFS, a Session URL would be identical to the ordinary resource URL and is therefore not generated.

Anyone possessing an unexpired Session URL can use the associated access. Avoid logging it, placing it in public pages, or sending it over an unencrypted connection.

JSON/HTTP service

The WFS browser client and NetIo use a small REST-style HTTP service. Applications normally use the supplied clients rather than calling these operations directly. Directory paths should end with /.

Directory listing contract

GET directory/?cmd=lj returns application/json containing an array. Every entry has the following stable fields:

[
  {"n":"documents", "s":-1,  "t":1785580746},
  {"n":"notes.txt", "s":319, "t":1785580800}
]
n
File or directory name relative to the requested directory.
s
File size in bytes, or -1 for a directory.
t
Last-modified time as Unix epoch seconds.

The meanings and types of n, s, and t are part of the NetIo compatibility contract and must not be changed. Future versions may add fields that older clients can ignore. Internal directories such as .LOCK and .DAV are excluded.

Directory commands

CommandArgumentsPurpose
ljNoneList a directory using the stable JSON format above.
mkdirtdirCreate a directory.
mvfrom, toRename or move a resource within the WFS.
rmtfileLegacy command for deleting a named resource. New clients use HTTP DELETE.
getlocknameReturn lock owner and expiration information for one resource.
getlocksRepeated nReturn lock state for multiple files.
locktime, repeated nLock files until the supplied Unix epoch time.
unlockRepeated nUnlock files.
sesuriNoneReturn the resource's ordinary URL or an enabled Session URL.

Commands may be supplied in the query string or as form data. Successful mutation responses use {"ok":true}. Errors use {"err":"code","emsg":"description"} with an appropriate HTTP status. An unknown command returns badcmd; a commandless directory request on a JSON-only WFS returns missingcmd.

HTTP methods

Other applicable methods are offered to the embedded WebDAV server, including PROPFIND, MKCOL, COPY, MOVE, LOCK, and UNLOCK.

NetIo compatibility

NetIo presents a remote WFS as an I/O interface. It uses:

A NetIo base URL must identify a WFS directory. NetIo probes and normalizes directory URLs with a trailing slash, and the standard WFM similarly canonicalizes direct directory URLs. NetIo can authenticate normally, or it can use an enabled Session URL as its base URL.

Web File Manager ES module

The WFM is a reusable client-side ES module for modern browsers and does not generate its interface on the server. The host page supplies one HTML element, imports the module, and calls mount. The WFS URL must have the same origin as the embedding page.

Minimal embedded manager

<div id="files"></div>
<link rel="stylesheet" href="/rtl/wfm/wfm.css">

<script type="module">
  import {mount} from "/rtl/wfm/wfm.js";

  const files = mount(document.getElementById("files"), {
    url: "/fs/"
  });

  files.ready.catch(console.error);
</script>

This example mounts the core manager only. The host page retains ownership of the surrounding layout and may override the WFM CSS custom properties.

mount(host, options)

host
The HTML element whose contents are replaced by the WFM.
options.url
Base URL of the WFS. The default is /fs/. The URL must be same-origin.
options.path
Initial directory relative to the WFS root, for example /documents/. It defaults to the page's wfm query argument and then to /.
options.history
When true, directory navigation updates the browser path with the History API, and Back/Forward navigation reopens the corresponding directory. Use this for a page served directly from WFS, not for a host page whose URL belongs to another application route.
options.plugins
Array of plugin functions activated after the core manager is created.

Manager object

manager.ready
Promise for the initial directory load.
manager.open(path)
Open a directory and return a promise.
manager.refresh()
Reload the current directory.
manager.selection()
Return the currently selected entries.
manager.destroy()
Abort pending work, run plugin cleanup, remove listeners, close the dialog, and empty the host element.

Client plugins

A plugin is a JavaScript function receiving the small WFM API. It may register commands or previews and may return a cleanup function. Plugins are assembled entirely on the client; WFS does not load a plugin manifest or execute plugin-specific server operations.

The standard module exports two opt-in plugins:

<div id="files"></div>
<link rel="stylesheet" href="/rtl/wfm/wfm.css">

<script type="module">
  import {mount, search, text} from "/rtl/wfm/wfm.js";

  const files = mount(document.getElementById("files"), {
    url: "/fs/",
    plugins: [search, text]
  });

  files.ready.catch(console.error);
</script>

The plugin API provides add("command", spec), add("preview", spec), list(path), open(path), refresh(), url(entry, options), selection(), directory(), and the shared ui.open()/ui.close() dialog surface. A function returned by add unregisters the extension.

Styling

The default stylesheet is scoped below .wfm. An application may replace it or override these supported custom properties:

The standard page produced by wfs.wfm() occupies the complete viewport. Embedded managers retain the normal border, corner radius, and configurable height so they fit into a larger application.