-
-
Notifications
You must be signed in to change notification settings - Fork 911
Modules PublicIp
Print your public IP address and related information
| Module type | publicip |
| Default order | 49 (only used by --gen-config) |
| Module source | src/modules/publicip/publicip.c |
| Detection source | src/detection/publicip/publicip.c |
Asks a remote service which address the world sees, and prints it. With the default service the geolocation that comes back in the same response is appended:
Public IP: 203.0.113.42 (Berlin, DE)
Without a location the line is just the address. The module performs a real HTTP request, so it needs network access and it is the slowest module in a default run.
Platform-independent: src/detection/publicip/publicip.c is compiled unconditionally and sits on top
of common/networking, which has a per-platform backend (networking_linux.c,
networking_windows.c, …). There is no nosupport variant.
| Key | Type | Default | Description |
|---|---|---|---|
url |
string | "" |
Service to ask. Empty means the built-in default (see below) |
timeout |
integer (ms) | 0 |
Connection timeout in milliseconds; 0 disables it |
ipv6 |
boolean | false |
Resolve and connect over IPv6, and use the IPv6 variant of the default service |
key |
string | Public IP |
Module key. A single space hides the key and the separator |
keyColor |
color | – | Overrides display.color.keys
|
keyIcon |
string | built-in glyph | Printed when display.key.type includes the icon bit. Any glyph works; "" prints none. |
keyWidth |
integer | – | Overrides display.key.width
|
outputColor |
color | – | Overrides display.color.output
|
format |
string | – | Custom output format (see below) |
condition |
object | – | Show the module only if the conditions match |
With url empty the module connects to ipinfo.io (or v6.ipinfo.io when ipv6 is true) on
port 80, requests /json, and parses the reply as JSON: the ip field becomes the address and
city + country become the location, joined with , .
With a url set, the whole response body — headers stripped, trailing whitespace trimmed — is the
address, and location stays empty. The reply is not parsed as JSON in that case, so a service that
returns a JSON object prints the object verbatim; use a service that answers with a bare address
(http://api.ipify.org, http://icanhazip.com, …).
url only accepts http. The scheme prefix is optional, but any other scheme aborts the run — see
the pitfalls. An optional :port is honoured (default 80), and an IPv6 literal has to be bracketed:
[2001:db8::1]:8080.
timeout is in milliseconds and only applies when it is greater than 0; 0 leaves the socket at
the system default, which can make an unreachable service hang for a long time.
Run fastfetch -h publicip-format for the authoritative list.
| Variable | Description |
|---|---|
{ip} |
Public IP address |
{location} |
Location |
Neither is marked * in the help output, so neither is available in the key format.
{location} is empty whenever url is set, and also when the default service's city / country
fields are missing.
- On failure the object is
{"type": "PublicIp", "error": "…"}instead, with noresultkey. - Both fields are strings;
locationis""(nevernull) when unknown. - The key order inside
resultisip, thenlocation.
// Address only, no geolocation
{ "type": "publicip", "format": "{ip}" }// A bare-address service, with a 2 second timeout
{ "type": "publicip", "url": "http://api.ipify.org", "timeout": 2000 }// Both families in one run -- this is the only way to have two instances
{ "modules": [ { "type": "publicip" }, { "type": "publicip", "ipv6": true } ] }The module has no TLS support at all, and its URL parser is deliberately tiny. Whenever the
endpoint is https, or its URL is anything the parser does not accept (a redirect target, a
non-default port on an IPv6 literal, a URL that needs authentication), the answer is to stop using
this module and use Command with curl instead. Modules-Weather sits on the same
networking layer with the same lack of TLS, but it has no url key at all — its host is fixed — so
the workaround below is the only way to reach any other endpoint from it:
{
"type": "command",
"key": "Public IP",
"text": "curl -s --max-time 5 https://api.ipify.org"
}That produces the same line the publicip module would:
Public IP: 203.0.113.42
Points that matter when writing such a module:
-
shellandparamdefault to the platform shell —cmd.exe /con Windows and/bin/sh -celsewhere — sotextis written exactly as it would be typed into a shell.curlships with Windows 10 and later, so nothing has to be installed there. -
Always pass an explicit timeout. The
commandmodule has no timeout of its own, and an unreachable HTTPS endpoint makes curl sit for its own (much longer) default.--max-time 5keeps a failure from stalling the whole run. -
A failed request is reported, not hidden. curl exits non-zero on a connection or TLS error and
the module then prints
Child process exited with an error— which, like every module error, needsdisplay.showErrorsto betrueto be visible. -
The whole body becomes the value, so a service that answers with more than the address (a JSON
object, or
ipip.net-style text carrying a location) needs either a service that returns a bare address or aformatthat trims it. UsesplitLines: truewhen the body has several lines. -
The JSON result is the raw string under
result, so a script that consumedPublicIp.result.iphas to readCommand.resultinstead.
-
The module can only be used once per address family. Two
publicipmodules with the sameipv6value make fastfetch printError: PublicIp module can only be used once due to internal limitationsto stderr and exit with status 1 before printing anything — including when the two modules are otherwise identical. Oneipv4plus oneipv6instance is fine, because they occupy separate slots.Modules-Weatheris the stricter version of the same restriction: it has a single slot and no address-family split, so its second instance always aborts the run. -
A
urlwith an unsupported scheme kills the whole run.https://…printsError: only http: protocol is supported. Use \Command` module with `curl` if needed` and exits with status 1, before anything else is printed. There is no TLS support here and no way to relax the check — see Using HTTPS and other unsupported URLs for the recommended replacement. -
A malformed port or an unbalanced
[does the same. Invalid values in the URL are reported withinvalid port,unmatched '['orunexpected characters after the IPv6 literaland exit with status 1. Bare IPv6 literals without brackets are treated as having no port, sohttp://2001:db8::1is not parsed as host + port and will not resolve as intended. -
The request is sent while the configuration is parsed, not when the module runs.
ffPreparePublicIp()issues the HTTP request as soon as the module is seen in the config (or in--gen-config-style preparsing), and the response is only read later, when the module is actually printed. A run that aborts before reaching the module still performed the request.Modules-Weatheris built the same way, so the two modules share this timing. -
The default service only speaks plain HTTP on port 80. There is no fallback and no retry; if the
network blocks it, the module reports the connection error. A custom
urlalso has to be plain HTTP for the same reason. -
A service that answers with an empty body yields
Empty server response received, which is an error, not an empty line. This is a common outcome for a URL that redirects or that requires HTTPS. -
locationis a free-form"city, country"string. It is built from whatever the default service's JSON contains; entries that are missing become the empty string, and the join still emits the,. Nothing validates or normalises the country code. -
The
urldefault documented indoc/json_schema.jsondoes not match the runtime. The schema advertiseshttp://ipinfo.io/ip, but the actual default is an empty string, which selectsipinfo.io/jsonand therefore also produceslocation. Setting the URL tohttp://ipinfo.io/ipexplicitly gives a different result: no location at all. -
Errors are invisible by default. Connection and parse failures go through
ffPrintError(), so withdisplay.showErrorsat its defaultfalsethe module simply prints nothing.
FFPublicIpResult holds two strings, ip and location. The module keeps two static
FFNetworkingStates — one per address family — and two static status pointers, which is exactly why
the module cannot be instantiated twice for the same family.
ffPreparePublicIp() fills in state->timeout and state->ipv6 and calls
ffNetworkingSendHttpRequest(). With no url it enables HTTP compression and TCP Fast Open and asks
ipinfo.io:80 for /json. With a url it strips an optional http:// prefix (any other scheme
aborts the process), splits the path off at the first /, splits an optional port off the host —
from inside [...] for a bracketed literal, or after the single colon of an unbracketed host — and
then issues the request with path / when none was given.
ffDetectPublicIp() reads the response, resets the state and the status so the slot becomes reusable
in a later round, skips everything up to the first \r\n\r\n to drop the headers, and then either
parses the JSON (default service only) or moves the trimmed body into ip. When the default service's
reply is not valid JSON the raw body is used as the address, so a plain-text answer from a custom
endpoint also works.
[ { "type": "PublicIp", "result": { "ip": "203.0.113.42", "location": "Berlin, DE" } } ]