External Access (v0.14.0+)

The “External Access” feature opens a local HTTP port that can be used to interact with an lnav instance outside of the TUI. The server is started automatically when lnav runs with the TUI (but not in headless or secure mode) on a port picked by the OS and with a random API key. The :external-access [–name] [port] [api-key] command can be used to pick the port, API key, or instance name instead. Once the port is open, HTTP requests can be sent to access static files, execute commands, or poll for changes. When the external port is open, a globe icon (🌐) is displayed in the top-right corner. Clicking that icon will open a URL in a browser and log you into the server. The :external-access-login [app] command can also be used to login.

Clients that poll the server for changes, like a browser page or an editor plugin, are counted next to the globe (e.g. 🌐 2), and a message is shown when a new one connects. A browser is identified by its login session, an editor plugin by its client_id, and anything else by its User-Agent. Since the requests do not keep a connection open, a client stops being counted when it has not polled for 15 seconds. Requests that do not poll, like a script sending a command, are not counted.

Note

The server only binds to localhost, so it will not be accessible over the network. If you need to access lnav remotely, consider using SSH forwarding.

Discovery

So that clients, like editor plugins, can find a running lnav without being configured with a port and key, each instance with an open port writes a discovery file to external-access/<pid>.json in lnav’s configuration directory (usually ~/.lnav). The directory is only accessible by the user and the file is removed when lnav exits. Files left behind by instances that did not exit cleanly are removed the next time an instance starts. The file contains the paths given on lnav’s command-line, along with its name and current directory, to make it easier to tell instances apart. The contents are described by the following schema:

https://lnav.org/external-access-instance-v1.schema.json

https://lnav.org/external-access-instance-v1.schema.json

Written by each running lnav whose external-access server is open, so clients can find and connect to it.

properties

  • $schema

/$schema

The URI that specifies the schema that describes this type of file

type

string

examples

https://lnav.org/external-access-instance-v1.schema.json

  • pid

/pid

The ID of the lnav process. Clients should ignore the file if this process is not running.

type

integer

  • version

/version

The version of lnav

type

string

  • name

/name

The name of the instance, set with the –name option of the :external-access command. Defaults to the name of the directory lnav was started in.

type

string

  • cwd

/cwd

The current working directory of lnav, which is updated by the :cd command

type

string

  • paths

/paths

The paths given on the command-line. Local paths and glob patterns are absolute; remote paths and URLs are as given.

type

array

items

type

string

  • url

/url

The base URL of the external-access server

type

string

  • port

/port

The port number of the external-access server

type

integer

  • api_key

/api_key

The API key, before Base64 encoding. Send it Base64-encoded in the X-Api-Key header.

type

string

  • started

/started

When the server was started, in seconds since the epoch

type

integer

additionalProperties

False

When there are multiple instances, a client can pick the one to talk to by matching the name or cwd against its own project. The instances list subcommand of the management CLI does this matching and skips files whose process has exited. For example, to get the URL of the instance started in the current directory, or the closest one beneath it:

lnav -m instances list --cwd . -o url | head -1

The GET /api/version response also includes the name and cwd so a client can check that it reached the instance it expected.

Authentication

All requests to lnav’s external access server are authenticated. A request must have one of the following:

  • An X-Api-Key header with the Base64-encoded value of the API-key that was passed to the :external-access command or that was written to the discovery file. This header should be used for automations.

  • An lnav_session_id cookie. This cookie will be set through the flow initiated by the :external-access-login [app] command that opens the /login URL using the configured external-opener. The /login URL accepts a one-time-password query parameter and, if it matches, the session cookie will be created. A single one-time-password is possible at any time and sessions are only valid for the current invocation of lnav.

Endpoints

The following routes are available:

  • GET /api/version

    Get the version, process ID, name, and current working directory of the lnav instance.

  • POST /api/exec
    Content-Type: text/x-lnav-script

    Execute an lnav script and receive the resulting output. Values can be passed to the script in headers instead of being quoted into its text: a X-Lnav-Var-<name> header whose value is the Base64-encoded value sets the variable $<name>. The prefix is matched without regard to case; the rest of the header name is the variable name as sent. In SQL statements, the variable is a bound parameter, and in commands it expands to a single argument.

    The results of SQL statements in the script go to a DB view of the request’s own, so a client’s queries don’t replace what the user has in the DB view. To put them in the regular DB view instead, for example to show them with :spectrogram, send an X-Lnav-Db-View: shared header.

  • POST /api/poll
    Content-Type: application/json

    Perform a long-poll of lnav’s TUI state. The first request to this API should be a null to get the current state. The response contains the following fields:

    • next_input - Subsequent calls should send this object so the server knows when there has been a state-change with respect to this client. Currently, the view_states/log_selection field is the only stable field and refers to the focused message in the LOG view. The log_index_seq field is the log_index.seq from the previous response.

    • background_tasks - A list of background task progress updates.

    • log_index - What changed in the LOG view’s index since the log_index_seq that was sent, so a client can re-query only the rows that changed (e.g. with log_line >= from_row against all_logs). It contains:

      • seq - The current change sequence number. A script can read it in the same snapshot as its queries with jget(view_details, '/index-seq') from the log row of lnav_views, so it can send that instead and skip changes its query already saw.

      • row_count - The number of rows in the LOG view.

      • reset - True when the changes since the given sequence number are not available (the first poll, or too many changes since then); treat every row as changed.

      • changes - A list of changes, oldest first. Each has a seq, the index generation, from_row, and row_count: every row from from_row onward was added or replaced, leaving row_count rows. Newly appended lines have a from_row equal to the previous row count. A rebuild or filter change starts earlier, possibly at zero.

    • open_requests - Files for an editor client to open; see below.

    An editor, like an IDE plugin, can ask to open the files that lnav would otherwise pass to an external editor command, which can be much faster for IDEs that are slow to launch. To do so, it adds these fields to the object it sends:

    • client_id - A string that identifies the client across polls.

    • editor_roots - The directories, as lnav sees them, that the client can open files under.

    When lnav needs to open a file under one of those directories, it adds an entry to the open_requests of the client with the closest root. Each entry has an id, the path, and the line and col to go to, which are one-based. The last_event_id in next_input is the highest id sent; a request is sent again until a poll comes back with that last_event_id. If no client has a matching root, the external editor command is used instead. If the client does not receive the request within two seconds, the request is dropped.

  • GET /assets/css/theme.css

    Get the CSS rules for the classes used in the 'html' output of the lnav_view_lines() SQL function. The rules are generated from the current theme, so the HTML looks like the lines on the screen. The pages rendered from Markdown, including apps, already link to this stylesheet. The same rules are returned by the lnav_theme_css() SQL function.

Apps

To support custom user-interfaces on top of lnav, “apps” can be installed that are reachable via the external access server. These browser-based apps can provide a rich interface for executing queries and presenting their results. For example, a dashboard for access_log files can display multiple charts for interesting request statistics. Apps are reachable from the landing page for the external access server or you can open one directly by passing its ID to the :external-access-login [app] command. An app ID has the form <publisher>/<app-name> (e.g. lnav/api-test).

Creating an app can be done using the management CLI, like so:

lnav -m apps create mydash

This command creates a directory in the configs directory and populates it with the necessary configuration file and a sample index.md file.

Test Harness

The lnav/api-test app is included by default as a demonstration of the external access server APIs.

Reference

The following are the configuration properties necessary to define an app:

/apps

Application definitions

type

object

patternProperties

  • ^([\w\-]+)$

/apps/<publisher>

The publisher of the application

type

object

patternProperties

  • ^([\w\-]+)$

/apps/<publisher>/<app_name>

The application definition

type

object

properties

  • root

/apps/<publisher>/<app_name>/root

The path to use as the root for files that are a part of this application. The path is relative to the location of the file containing this property.

type

string

pattern

[\w\-\.]+

  • description

/apps/<publisher>/<app_name>/description

A description of this application

type

string

additionalProperties

False

additionalProperties

False

additionalProperties

False