FTP connections preserve passwords and filenames, with CI checking every change #1

Merged
temeddix merged 15 commits from fix-directory-listing into main 2026-09-30 13:10:40 +00:00
3 changed files with 18 additions and 22 deletions
Showing only changes of commit cd4b4c5e70 - Show all commits
+7 -10
View File
@@ -8,17 +8,16 @@ The plugin is a WebAssembly component built against the Memona filesystem
contract, `memona:filesystem@0.1.0`. It uses only the host's sockets; it has no
ambient filesystem, environment or network access of its own.
This package requires a Memona version that supports plugin manifest schema 2.
## What it registers
One provider, `ftp`, for the `ftp` and `ftps` schemes. A connection is
configured with a server name, an optional port, a TLS toggle for FTPS, and an
optional username and password. An empty username signs in anonymously.
In Memona's connection permissions, enter the server's hostname or IP in
**Allowed hosts**, without a scheme, port or path. An empty list blocks the
connection. Turn on **Private network** as well for localhost or a private
network; this switch does not grant a host by itself. Save and reconnect after
changing permissions.
For localhost or a server on a private network, turn on **Private network** in
the connection settings. Save and reconnect after changing these settings.
The TLS toggle currently starts TLS on the control connection only. Protected
data connections and explicit `AUTH TLS` negotiation are not implemented, so
@@ -37,11 +36,9 @@ and connects out to it; `PASV` fallback is not implemented. The server picks a f
transfer and announces it only as the transfer begins, so the range that has to
be reachable is wide.
`manifest.json` declares `networkPorts` as 21 and 1024-65535, and `networkHosts`
as `*` because a server can be anywhere. Declaring is not granting: a
connection reaches only the host and ports the person grants it, and a grant
never accepts a wildcard host. Grant the control port and the span the server
uses for its data channel, usually `21` and `1024-65535`.
The package declares socket ports 21 and 1024-65535, which Memona applies
automatically to each connection. The control port and passive data ports
must be within these ranges.
## Build
+1 -2
View File
@@ -1,5 +1,5 @@
{
"schemaVersion": 1,
"schemaVersion": 2,
"id": "memona/ftp",
"name": "FTP",
"version": "0.1.3",
@@ -7,7 +7,6 @@
"description": "Open an FTP server as a folder, and let agents work in it.",
"license": "MIT",
"entry": "plugin.wasm",
"networkHosts": ["*"],
"networkPorts": [
{ "from": 21, "to": 21 },
{ "from": 1024, "to": 65535 }
+10 -10
View File
@@ -35,10 +35,10 @@ interface host {
/// `respond`. An empty result means the body is complete.
pull: func(limit: u32) -> result<list<u8>, host-error>;
}
/// Begins a request to a host both declared by the package and granted on
/// the connection. Each redirect and DNS result is checked, and DNS is
/// pinned. Private/LAN addresses additionally require the private-network
/// grant. Cross-origin redirects remove request headers; HTTPS cannot
/// Begins a request to any public HTTP or HTTPS host. Each redirect and
/// DNS result is checked, and DNS is pinned. Private/LAN addresses require
/// the connection's private-network grant. Cross-origin redirects remove
/// request headers; HTTPS cannot
/// downgrade. The host bounds total bytes per exchange, not buffer size.
http: func(request: request) -> result<exchange, host-error>;
@@ -51,13 +51,13 @@ interface host {
/// Writes at most 1,048,576 bytes and returns how many were accepted.
write: func(data: list<u8>) -> result<u32, host-error>;
}
Outdated
Review

LAN and loopback access still requires Memona's Private network permission, so this contract comment incorrectly promises direct access. Restore that qualification here and in the README so users are not led to a setup that fails at runtime.

Profile: codex · gpt-5.6-terra

LAN and loopback access still requires Memona's Private network permission, so this contract comment incorrectly promises direct access. Restore that qualification here and in the README so users are not led to a setup that fails at runtime. > Profile: `codex` · `gpt-5.6-terra`
/// Opens a socket to a host and port both declared by the package and
/// granted on the connection. The host resolves and pins DNS, applies the
/// same private-address rule as `http`, and starts TLS when asked. Ports
/// negotiated at runtime, such as an FTP data channel, still need a grant.
/// Opens a socket to any public host on a package-declared port.
/// The host resolves and pins DNS, applies the same private-address rule
/// as `http`, and starts TLS when asked. Ports
/// negotiated at runtime, such as an FTP data channel, must be declared.
connect: func(host: string, port: u16, tls: bool) -> result<tcp, host-error>;
/// One datagram socket connected to a single granted peer.
/// One datagram socket connected to a single peer.
resource udp {
/// Sends one datagram of at most 65,507 bytes.
send: func(data: list<u8>) -> result<u32, host-error>;
@@ -65,7 +65,7 @@ interface host {
/// means none arrived inside the host's wait, not that the peer left.
receive: func(limit: u32) -> result<list<u8>, host-error>;
}
/// Connects a datagram socket under the same declaration and grant rules
/// Connects a datagram socket under the same port and private-network rules
/// as `connect`, for the few filesystem protocols that still need one.
bind: func(host: string, port: u16) -> result<udp, host-error>;