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.
local wfs = require"wfs"
Loading the module performs two actions:
ba.create.wfs.wfs.create and wfs.wfm.| Construction | Commandless directory GET | Typical 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.
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.
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.
name"fs" for a server mounted at /fs/.priority0.iolockdir.LOCK keeps it out of ordinary directory listings.maxuploads5.maxlocks20. When specifying limits, provide both numbers.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.
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.
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.
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.
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.
app.files:setauth(authenticator, authorizer)
app.files:configure{tmo=15*60}
Configures optional WFS behavior. The recognized fields are:
tmotmo to zero or omit it to disable Session URLs.filterservice(_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.pageaccessdeniedGET 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
}
The method returns a table containing the WFS io object and its authorization function for advanced integrations.
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.
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.
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 /.
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}
]
ns-1 for a directory.tThe 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.
| Command | Arguments | Purpose |
|---|---|---|
lj | None | List a directory using the stable JSON format above. |
mkdirt | dir | Create a directory. |
mv | from, to | Rename or move a resource within the WFS. |
rmt | file | Legacy command for deleting a named resource. New clients use HTTP DELETE. |
getlock | name | Return lock owner and expiration information for one resource. |
getlocks | Repeated n | Return lock state for multiple files. |
lock | time, repeated n | Lock files until the supplied Unix epoch time. |
unlock | Repeated n | Unlock files. |
sesuri | None | Return 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.
HEAD returns stat information. Directories include BaIsDir: true; WFS responses also include HttpResMgr: V2.1 and a hexadecimal modification-time ETag.GET reads a file. The service supports the resource-reader behavior required for ranged NetIo reads. Add download=1 to request an attachment response.PUT creates or replaces a file using the asynchronous upload service.DELETE deletes a file or recursively deletes a directory.POST accepts directory commands and multipart/form-data uploads.Other applicable methods are offered to the embedded WebDAV server, including PROPFIND, MKCOL, COPY, MOVE, LOCK, and UNLOCK.
NetIo presents a remote WFS as an I/O interface. It uses:
cmd=lj and the mandatory {n,s,t} fields for directory iteration;HEAD, BaIsDir, HttpResMgr, Content-Length, and ETag for stat operations;GET with byte ranges for reads;PUT for writes;DELETE for file and directory removal; andmkdirt and mv for directory creation and rename/move.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.
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.
<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.
hostoptions.url/fs/. The URL must be same-origin.options.path/documents/. It defaults to the page's wfm query argument and then to /.options.historyoptions.pluginsmanager.readymanager.open(path)manager.refresh()manager.selection()manager.destroy()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:
search recursively searches file and directory names using cmd=lj;text previews .txt files in the WFM dialog.
<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.
The default stylesheet is scoped below .wfm. An application may replace it or override these supported custom properties:
--wfm-bg--wfm-color--wfm-border--wfm-accent--wfm-muted--wfm-heightThe 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.