Network requests
Declare network and list your servers to send HTTPS requests to them. The main process makes each request and returns the status and text of the reply. It needs FluxPlugs 0.5.0-beta.1 or later.
List your hosts
Section titled “List your hosts”Add a network field next to the capability:
{ "capabilities": ["settings", "network"], "network": { "hosts": ["api.example.com", "cdn.example.com"] }}The field is required exactly when capabilities lists network; otherwise the manifest is refused with invalid_plugin_network. List 1 to 16 unique hosts, each a lowercase public DNS name with at least two labels: no IP addresses, wildcards, ports or trailing dot. Names under localhost, local, localdomain, internal, lan, home, test, invalid, example, onion and arpa are refused.
The hosts are part of what the user trusts. The enable prompt lists them under Connects to, and a plugin whose hosts change needs to be confirmed again, like one whose capabilities change.
Send a request
Section titled “Send a request”Requests need no user action. fetch resolves with the reply, including 4xx and 5xx replies; it rejects only when the request is refused or doesn’t finish.
try { const response = await api.network.fetch({ url: "https://api.example.com/v1/status?lang=" + encodeURIComponent("en"), headers: { Accept: "application/json" }, }); if (response.status === 200) { const data: unknown = JSON.parse(response.body); api.logger.info("Status loaded", { object: typeof data === "object" }); } else { api.logger.warn("Status request failed", { status: response.status }); }} catch (error) { api.logger.warn("Status request refused", { code: error instanceof Error ? error.message : "unknown", });}For a POST, pass the body as a string and set its Content-Type. Without one, FluxPlugs sends text/plain;charset=utf-8.
async function translate(text: string): Promise<string | undefined> { const apiKey = await api.settings.get<string>("apiKey"); const response = await api.network.fetch({ url: "https://api.example.com/v1/translate", method: "POST", headers: { "Content-Type": "application/json", Authorization: "Bearer " + (apiKey ?? ""), }, body: JSON.stringify({ text, target: "en" }), }); if (response.status === 429) return undefined; if (response.status < 200 || response.status >= 300) throw new Error("http_" + response.status); const data = JSON.parse(response.body) as { translation?: unknown }; return typeof data.translation === "string" ? data.translation : undefined;}
try { const translated = await translate("Hola"); if (translated !== undefined) await api.ui.notify(translated);} catch { await api.ui.notify("The translation service didn't answer.");}Keep an API key in a secret setting, and never log it or show it in a notice.
Rules and limits
Section titled “Rules and limits”- URL: HTTPS only, at most 8 KiB, with no credentials or port. The host must be one the user confirmed. The fragment is dropped.
- Addresses: the host must resolve to public IPv4 addresses. FluxPlugs pins the address it checked and refuses private and reserved ranges. IPv6-only hosts and system proxies aren’t supported.
- Headers: at most 16, with printable ASCII values of up to 2048 characters. FluxPlugs sets User-Agent: FluxPlugs and Accept-Encoding: identity. You can’t set host, cookie, cookie2, set-cookie, origin, referer, connection, keep-alive, content-length, transfer-encoding, te, trailer, upgrade, expect, via, date, dnt, forwarded, accept-encoding, accept-charset, user-agent, x-http-method, x-http-method-override or x-method-override, or any name starting with proxy-, sec-, x-forwarded- or access-control-. Names are compared without case.
- Cookies: none are sent or stored.
- Body: only with POST, at most 32 KiB of UTF-8.
- Reply: at most 256 KiB of UTF-8 text. Compressed and binary replies are refused. The result has status, contentType when the server sent one, and body.
- Redirects: up to 3, each checked like the original URL. A 303, or a 301 or 302 after a POST, continues as a GET without body. Headers you set are dropped when a redirect leads to another host.
- Time: 10 seconds for the whole request, including DNS, redirects and the reply.
- Concurrency: 4 requests in flight per plugin instance, and 32 plugin instances with requests in flight. Stopping the plugin cancels its requests.
| Error | Meaning |
|---|---|
invalid_network_request |
Malformed request, http URL, forbidden header, GET body, too big |
network_host_denied |
The host, or a redirect’s host, isn’t in the confirmed list |
network_redirect_denied |
A redirect to a non-HTTPS URL or one with credentials or a port |
network_redirect_limit |
More than 3 redirects |
network_private_address |
The host resolves to a private or reserved address |
network_timeout / network_cancelled |
Took longer than 10 seconds, or the plugin stopped |
network_response_too_large |
The reply is over 256 KiB |
network_response_not_text / network_encoding_unsupported |
The reply isn’t UTF-8 text, or it is compressed |
network_limit |
Too many requests in flight; wait for earlier ones |
network_failed |
The connection failed or broke off |
invalid_network_response |
The host’s answer failed validation |
capability_denied:network |
The manifest doesn’t declare network, or it isn’t confirmed |
plugin_stopped / plugin_changed |
The instance stopped or its code changed; ignore the result |
Privacy
Section titled “Privacy”Everything a request carries reaches the listed servers without the user seeing it. A plugin that also has decorations or message_content could send the ids or text of messages on screen, and the enable prompt warns about that combination. Send only what the feature needs, and say in your description which service you use and what you send to it.
