The HTTP Filesystem Protocol provides a RESTful interface for performing POSIX-like filesystem operations over HTTP. It enables hierarchical file and directory manipulation using standard HTTP methods with filesystem-specific metadata encoded in HTTP headers.
- RESTful Interface: HTTP methods map directly to filesystem operations
- Metadata in Headers: File attributes encoded as HTTP headers
- Path-based URLs: Filesystem paths map directly to URL paths
- Directory Listings: Directory contents encoded as plain text
- Atomic Operations: Individual operations are atomic
Filesystem paths map directly to HTTP URLs:
- Base URL:
https://example.com/fs - File path:
/path/to/file.txt→https://example.com/fs/path/to/file.txt - Directory path:
/path/to/dir→https://example.com/fs/path/to/dir
Note: Directory paths MAY end with / as a convenience to automatically set Content-Type: application/x-directory.
Retrieves file content or directory listing.
Request:
GET /path/to/file.txt HTTP/1.1Response (File):
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 1024
Content-Mode: 33188
Content-Modified: 1641024000
Content-Ownership: 1000:1000
[file content]Response (Directory):
HTTP/1.1 200 OK
Content-Type: application/x-directory
Content-Length: 45
Content-Mode: 16877
Content-Modified: 1641024000
Content-Ownership: 1000:1000
file.txt 33188
subdir 16877Retrieves file/directory metadata without content.
Request:
HEAD /path/to/file.txt HTTP/1.1Response:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 1024
Content-Mode: 33188
Content-Modified: 1641024000
Content-Ownership: 1000:1000Creates or completely replaces a file or directory.
Request (File):
PUT /path/to/file.txt HTTP/1.1
Content-Type: application/octet-stream
Content-Length: 12
Content-Mode: 33188
Content-Modified: 1641024000
Content-Ownership: 1000:1000
Hello World!Request (Directory):
PUT /path/to/dir HTTP/1.1
Content-Type: application/x-directory
Content-Length: 0
Content-Mode: 16877
Content-Modified: 1641024000
Content-Ownership: 1000:1000
Alternative (using trailing slash convenience):
PUT /path/to/dir/ HTTP/1.1
Content-Length: 0
Content-Mode: 16877
Content-Modified: 1641024000
Content-Ownership: 1000:1000
Response:
HTTP/1.1 200 OK
OKUpdates file/directory metadata without changing content.
Request:
PATCH /path/to/file.txt HTTP/1.1
Content-Mode: 33261Response:
HTTP/1.1 200 OK
OKRemoves a file or directory.
Request:
DELETE /path/to/file.txt HTTP/1.1Response:
HTTP/1.1 200 OK
OK- Purpose: Unix file mode (permissions + type)
- Format: Decimal string representation of Unix mode
- Required: No (defaults applied)
- Examples:
33188- Regular file with 0644 permissions16877- Directory with 0755 permissions33261- Executable file with 0755 permissions
- Purpose: Last modification timestamp
- Format: Unix timestamp (seconds since epoch) as decimal string
- Required: No (current time used if omitted)
- Example:
1641024000
- Purpose: File owner and group
- Format:
uid:gidformat - Required: No (defaults to
0:0) - Example:
1000:1000
- Purpose: MIME type indicator
- Values:
application/x-directory- Directoryapplication/octet-stream- Binary file (default)- Other standard MIME types as appropriate
- Required: No (auto-detected from path and content)
- Purpose: Size of content in bytes
- Format: Decimal string
- Required: Yes for PUT requests
- Behavior: Standard HTTP header
Directory contents are encoded as plain text with the format:
filename mode
dirname mode
Characteristics:
- One entry per line
- Space-separated name and mode
- Lexicographically sorted
- Unix mode in decimal format
- Terminated with newline
Example:
.hidden 33188
README.md 33188
bin 16877
src 16877
- Primary: Content-Type header (
application/x-directory) - Secondary: Mode value (directory flag in Unix mode)
- Convenience: Path ending with
/automatically sets directory content-type
200 OK- Operation successful404 Not Found- File/directory does not exist405 Method Not Allowed- HTTP method not supported412 Precondition Failed- Conditional request failed
Error responses include plain text descriptions:
HTTP/1.1 404 Not Found
Object Not FoundHTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, PUT, PATCH, DELETE
Method Not AllowedReading Files:
- GET retrieves content with metadata in headers
- HEAD retrieves only metadata
- Returns 404 if file doesn't exist
Writing Files:
- PUT creates/replaces entire file
- All metadata must be provided to preserve existing values
- Content-Length header required
Modifying Files:
- PATCH updates metadata only, content unchanged
- Only provided headers are updated
- Existing metadata preserved if not specified
Reading Directories:
- GET returns directory listing as plain text
- Content-Type is
application/x-directory - Entries sorted lexicographically
Creating Directories:
- PUT with
Content-Type: application/x-directoryheader - Empty or minimal content body
- Alternatively, PUT with path ending in
/automatically sets directory content-type
Directory Maintenance:
- Server automatically maintains parent directory listings
- Adding/removing files updates parent directory
- PATCH with Content-Mode updates entry in parent listing
Standard HTTP conditional headers supported:
If-Match/If-None-MatchIf-Modified-Since/If-Unmodified-Since
Standard HTTP range requests supported for partial file reads:
Range: bytes=0-1023
- Protocol is transport-agnostic regarding authentication
- Implementations should use standard HTTP authentication
- Access control is implementation-specific
- Implementations should validate paths to prevent directory traversal
- Relative path components (
.,..) require careful handling - Path injection attacks should be prevented
- MUST support GET, HEAD, PUT, PATCH, DELETE methods
- MUST detect directories via
Content-Type: application/x-directory - SHOULD treat paths ending with
/as convenience for setting directory content-type - MUST maintain directory listings automatically
- SHOULD support conditional requests
- SHOULD validate metadata format
- MUST set
Content-Type: application/x-directoryfor directory operations - MAY use trailing
/on directory paths as convenience - MUST send required headers on PUT operations
- MUST parse directory listing format correctly
- SHOULD handle standard HTTP error responses
- SHOULD support conditional requests
- Metadata header names are case-insensitive (per HTTP)
- Directory listing format is strict (space-separated, sorted)
- Unix mode values are decimal integers
- Timestamps are Unix epoch seconds
PUT /documents/readme.txt HTTP/1.1
Content-Type: text/plain
Content-Length: 13
Content-Mode: 33188
Content-Ownership: 1000:1000
Hello, World!GET /documents HTTP/1.1
# Response:
readme.txt 33188
scripts 16877PATCH /documents/script.sh HTTP/1.1
Content-Mode: 33261HEAD /documents/config.json HTTP/1.1
# 200 = exists, 404 = doesn't exist- Extended attributes (xattrs) support
- Symbolic link operations
- File locking mechanisms
- Bulk operations
- Directory watching/notifications
- No atomic multi-file operations
- No recursive directory operations
- Access time not currently supported
- No built-in versioning or conflict resolution
Thanks for the comments!
Since WebDAV deployments are still around, I wonder if it's safe to use LOCK with modern proxies and platforms. I'll look into it when I get around to locking.
Not that I know of. Old iterations used JSON, but seemed weird to use JSON for just this one thing. This is basically the simplest thing to satisfy io/fs.DirEntry.
They originally did! But it extended to the URL where you needed a trailing slash for directories. It started creating problems, so I removed it, and then that included in directory entries. I still support trailing slash as convenience, so maybe they can be supported here optionally as well. Since it is much easier to read as a human than the file mode.
That would be nice! You could ask the same thing of Unix. I didn't add this because my use case is actually mounting and using in a POSIX environment and you'd have to create them anyway.
Symlinks will be easy since its just a flag and the target path as the file contents (Linux used to do it this way).
My filesystem toolkit has fallbacks for move and copy, but it's also made to use the operation if it supports it. At the very least I want to support move/rename. I think the POST action pattern is reasonable, though ideally as a resource. Either way, I'd feel weird having only certain operations work this way.
COPY was added in WebDav right? If it turns out I can use custom verbs (specifically webdav), I'd use that for copy.
I could see PUT taking an argument for copy/move ... but also would the path be relative to the domain or the filesystem base path? Hmm.
Content-Ownership is a work in progress placeholder. My current use case will be protected by auth and I'd love it if POSIX user permissions could be enforced and used, but it gets tricky! Also considering using strings like Plan 9. Maybe it can support both and its up to you to do any mapping.
Definitely planning file watching. But I'm waiting to finalize the API I want to use in my toolkit first. I thought I started a discussion on Wanix for it, but I guess I haven't yet. Previous implementations at the HTTP level were just GET /path?watch to get change notifications. And at this point if there isn't a good format I have no reservations making something up if its "the simplest thing."