Lint.ai IPC Documentation

Website repository for https://ipcdocs.lint.ai/

Lint.ai IPC Documentation

This website hosts the IPC documentation for Lint.ai. If you are not developing modules for a Lint.ai implementation, it is unlikely to be of service to you; please visit Lint.ai for more information on the service or how to get started.

IPC format notes

The IPC protocol utilized by Lint.ai is exceedingly prescriptive, because it represents the boundary layer between code you can trust (your Lint.ai modules, Lint.ai itself) and code that could be coming from anyone allowed to make pull requests in your repository. Full details of the protocol are contained here, but briefly, conversations over the IPC socket look like this, repeated as many times as necessary:

Client: <verb> <arguments...>\n
Server: [OK|NO|ERR] <additional information>\n

To speak to the server, open a UNIX socket to /mnt/ipc/lgtm.ipc. This socket is created before the first module runs, and cannot be removed or manipulated in the execution context. Examples of how to open such a socket are available in the reference implemenation under the lib/ directory.

Verb List

Fetching a repo

These two work similarly. <short_GH_path> is easiest to explain by example; given a Github cloning path like https://github.com/lint-ai/lgtmlib.git or git@github.com:lint-ai/lgtmlib.git, the short path is lint-ai/lgtmlib.git.

<dest> is a name consisting only of ASCII characters in the ranges [a-zA-Z0-9.-] which does not start with a . or a -. The requested resource will be made available to the container at that location within the runtime_path.

When the checkout is completed, the response is OK, optionally followed by a space (\x20) and debugging data. If the request is invalid or malformatted, the response is NO, optionally followed by a space and debugging data. If an error occurs, the response is ERR, optionally followed by a space and debugging data.

OK clean checkout of lint-ai/lgtmlib.git is now at lgtmlib\n

NO too few arguments to GETPRIVATE\n

ERR Github says 404 Not Found\n

Approving changes (Github pull_request events)

The behavior of these verbs in a non-PR context is undefined. Always check your context via /L/is or similar.

STAMP causes the current PR or branch to be stamped as (to the best of the user’s ability) “approved” for merge. It only takes effect if the complete chain of modules finish successfully (i.e., clean shutdown with retval==0). STAMP always replies with OK I will try\n and does not (and can not, due to its design) guarantee that a stamp will actually occur.

If there is a STAMP pending, UNSTAMP stops it. If there is no stamp pending, it’s a no-op. The reply is always OK <debugging info>\n.

If there is a STAMP that has not been UNSTAMPED, and there is no pending REJECT, the reply will be OK STAMP\n or OK STAMP <debugging info>\n. In any other case, the word STAMP in the reply will be replaced with NOSTAMP.

Reject (request changes) on the PR. Unlike STAMP, once acknowledged, this SHOULD be honored even if other parts of the .lgtm fail. This is not foolproof, however; the nature of the transaction is such that intervening repo events—or various catastrophic internal failures in the LGTM runtime—may preclude us from explicitly rejecting the PR. However, an acknowledged REJECT does guarantee that if a PR review is left as a result of this particular LGTM run, it will request changes. The reply reflects this uncertainty, and is thus always OK I will try\n.

Sending a REJECT will mask (but does not remove) any STAMP; a subsequent UNREJECT will therefore have the practical effect of reinstating the old STAMPED-ness. If this is undesired, take care to both REJECT and UNSTAMP; the latter is a no-op in the worst case.

The <message> must conform to the permitted byte sequences, and is sent to Github as UTF8. Consider using REJECT64 in order to have complete message control. The message is optional, but users are cautioned that there may be social consequences to programmatically rejecting a PR without at least making an attempt to explain why you’ve done so.

Multiple REJECTs are accepted. A REJECT on an existing REJECT will cause the messages to be concatenated (in the order received) in the review comment accompanying the “request changes” review, each separated by two newlines (\n\n). If this behavior is not desired, UNREJECT first. Different methods of concatenation or merging of output—which is to say, coordinating amongst modules—is outside of the scope of the protocol.

Exactly as REJECT, but the message is base64-encoded.

As with UNSTAMP, so with UNREJECT. Note the caveat with regards to reverting to previous STAMPED-ness mentioned under REJECT.

Assuming success, the reply is OK PR purification proclaimed.\n.

As with STAMPED, but reflecting purely REJECT/UNREJECT status instead. A reply will start with either OK REJECT or OK NOREJECT.

Commits (Github push events)

The behavior of these commands outside of a COMMIT (Github calls them push) event is undefined. See /L/is for a way to check your current context.

Request a commit with the <message>. Takes effect based on the state of the repository checkout at the end of the run, and, as with STAMP, only if all other modules exit cleanly. Any intermediate commits are meaningless, and should generally be avoided; the commit sent to Github will always be exactly one ref deep and will contain all changes made relative to the original checkout.

A <message> must be provided. Since the protocol forbids embedded \n and many other useful characters (like :), most users will opt for COMMIT64. Indeed, this is what /L/commit uses under the hood.

If there is already a COMMIT, messages will be concatenated in the order received, separated by \n\n. To avoid this behavior, UNCOMMIT and then COMMIT.

If there are no changes after all modules have run, no commit will happen (and the message will be discarded). Thus, a message like chore(lint.ai): Automated fixups is perfectly valid (use of unpermitted characters notwithstanding), and will only appear in the commit log if any actual changes were made.

Reply is always OK I WILL TRY\n (barring errors, of course).

Exactly as COMMIT, but the message is base64-encoded.

If there is a commit pending, cancels it (and clears the message buffer). If there is no commit pending, it’s a no-op. In all cases, the reply is exactly OK apeirophobia acknowledged\n (barring errors).

As with STAMPED, reply will start with either OK COMMIT or OK NOCOMMIT.

Version info

Reply will be OK <protosemver> <server> <ssemver> <caps>\n, where <protosemver> is a SEMVER of the maximum version of this protocol supported by the server, <server> is a string that uniquely identifies the provider of the server (e.g. lint.ai), <ssemver> is a SEMVER of the server itself, and <caps> is reserved for future use.

Clients are encouraged, but not required, to utilize this command first, and to identify themselves in a manner corresponding to:

VERSION client=lgtmlib ver=1.0.0 uuvid=608a6dca816d9527bb81a042cc0f3ce31673de2c

uuvid is a subrelease-specific universally-unique identification of the client (a simple implementation of which would be the git SHA of the last commit to the repository, as shown here). Servers MAY reject requests without this identifying information by returning NO with human-readable information to explain the rejection intended to be shared with the operator. If a server does so, it MUST immediately close the client’s connection after sending this reply.

In future versions of this specification, arguments to and responses from VERSION are anticipated to be used in negotiating advanced protocol capabilities between clients and servers. Both client and server implementations are therefore encouraged to be conservative in their assumptions.

Protocol Considerations

Requests

The request language consists of verbs and arguments, terminated by newline (\n). Both verbs and arguments MUST NOT contain bytes outside the ASCII range [a-zA-Z0-9.=/+-], and MUST BE separated from one another and from the verb by exactly one space. Unimplemented verbs result in a NO response (but see below for an important caveat about using this behavior for protocol negotiation).

Requests are not guaranteed to be processed if their length exceeds 2,047 bytes.

Responses

Summary of response statuses
Response status Meaning !
OK The requested operation has been performed.
NO No work was done. Do not re-send this request.
ERR An error occurred while handling this request. Retries MAY be possible.
Response format

All requests, defined as a sequence of bytes terminated by a newline, regardless of syntactical or semantic integrity, MUST engender a response consisting of a single line of arbitrary non-null, non-\n bytes ending in \n, the first word of which is a status (OK, NO, or ERR) and the remainder of which is either specified by the verb definition above or is implementation-specific. Note that responses ARE NOT constrained by the character-set limits imposed on requests.

Response status

Clients MUST NOT rely on gradations between different information following NO or ERR responses to guide their behavior or to make inferences about internal server state. These semantics may change at any time and for reasons opaque to the client itself (e.g. because a different backend server has been hotswapped in). Servers MAY use ERR or NO without (or even with misleading) additional information to maintain a desired security posture. The client retry behavior distinction between ERR and NO SHOULD, however, always be honored if possible.

A client MUST NOT retry an operation resulting in a NO response without modification to the request itself (or a new session).

A client MAY retry a request beginning with ERR as many times as the server will allow.

NOOPs and other error cases

Any request (especially blank requests) MAY be processed by the server as a NOOP. Requests processed as a NOOP must receive a reply beginning with ERR or NO, depending on desired client retry behavior, that contains the keyword NOOP as the next token (ex.: NO NOOP, NO NOOP bad encoding, or ERR NOOP backend restarted please retry). Servers MAY NOT use NOOP if any part of the command may have been processed (i.e., a verb-handler called), to allow the client to distinguish between an ERR resulting from a verb failing to complete successfully (whereupon the client might be afraid to rely on state unless the verb is idempotent), versus a request which never invoked a verb handler at all.

A server MAY elect not to process a request, for example because it is too busy, because the request is blank, because the request exceeds the size of its internal buffers, or for any other implementation-specific reason. If this decision is made, the request is considered a NOOP, but the server MUST read all data until a \n is reached before sending an appropriate NOOP reply. In response to any other behavior (in particular, closing the session), a client MAY interpret the issue as a temporary error and retry accordingly.

Notes

While provision is made for enforcing limited buffer sizes in the server, responses are not limited in length in any way. Clients SHOULD take appropriate precautions if their request might generate a very large response body. Likewise, if a client’s connection to the server, or if the server itself, isn’t trusted or reliable, clients SHOULD be prepared to handle bizarre or malicious responses.

While a single connection can handle any number of VERBs and responses, a server MUST accept repeated dis- and re- connection, even in circumstances where connection-pooling would have been possible. Some modules—especially those relying on lgtmlib’s support for shell scripts—MAY open and close the socket repeatedly in the course of a single execution. All of this is normal and permissible. That said, the connection queue is only one-deep; parallelizing modules is possible, but IPC is not parallelizable at this time.

Protocol extensions

New capabilities MAY NOT be negotiated via any means other than the VERSION request and response. In particular, techniques such as reading the additional information that follows a verb other than VERSION, or using a verb without arguments or with intentionally-nonoperational arguments (such as --help) is considered a violation of the protocol, because it decreases implementation flexibility and increases the server operators’ monitoring burden when looking for misbehaving clients.

Client implementors are reminded that requests are limited to 2,047 bytes, and server implementors that there is no limit on the number of VERSION requests sent. Given that this enables an arbitrary duration of negotiations, creators of clients and servers MAY enforce limits, develop shorthands, extend buffers, or other techniques to speed processing. Any such changes are considered an implementation detail and left to each individual implementor’s discretion.