% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/spec.R
\name{jgd_spec}
\alias{jgd_spec}
\alias{jgd_protocol}
\title{jgd JSONL Protocol Specification}
\description{
The jgd device communicates with a rendering server over JSONL
(JSON Lines). Messages are exchanged over a persistent
connection using one of three transport protocols: Unix domain sockets
(Linux/macOS), Windows named pipes, or TCP.

This document specifies the wire protocol so that third-party servers
can implement a compatible rendering backend.
}
\section{Transport protocols}{


The client connects to the server using one of the following URI
schemes:
\itemize{
\item \verb{unix:///path/to/socket} -- Unix domain socket (Linux/macOS
default)
\item \verb{npipe:////./pipe/name} -- Windows named pipe (Windows default,
Docker-standard 4-slash form)
\item \verb{tcp://host:port} -- TCP socket (any platform)
}

Raw Unix socket paths (without a URI scheme) are also accepted.
}

\section{Message format}{


All messages are single-line JSON objects terminated by \verb{\\n}
(JSONL). Each message contains a \code{"type"} field identifying the
message kind. Encoding is always UTF-8.

Receivers should ignore unknown top-level fields in any message
(forward-compatible). Unknown \code{"type"} values should be silently
discarded rather than treated as errors.
}

\section{Coordinate system}{


All coordinates in drawing operations are in \strong{device pixels}
(i.e., \code{inches * dpi}). The origin \verb{(0, 0)} is the \strong{top-left}
corner of the device surface. The X axis increases to the right
and the Y axis increases downward.
}

\section{Connection handshake}{


The welcome message is \strong{deferred}: the server waits until it
receives the first message from R before sending it. This avoids
a race condition on Windows named pipes where writing before the
first read completes can cause data loss.

\if{html}{\out{<div class="sourceCode">}}\preformatted{R -> Server:  \{"type":"ping"\}
Server -> R:  \{"type":"server_info", ...\}
}\if{html}{\out{</div>}}

The server should also tolerate receiving a \code{frame} message
before \code{ping} (e.g., if a future client skips the ping). The
first received message of any type should trigger the deferred
welcome.
}

\section{Discovery file}{


The discovery file is an \strong{optional} JSON file that allows the
client to find the server without an explicit socket address. It
is a hint for auto-connection only; the welcome message is the
single source of truth.

\strong{Location} (platform-specific):
\itemize{
\item Linux: \verb{$XDG_CACHE_HOME/jgd/discovery.json}
or \verb{~/.cache/jgd/discovery.json}
\item macOS: \verb{~/Library/Caches/jgd/discovery.json}
\item Windows: \verb{\%LOCALAPPDATA\%/jgd/discovery.json}
}

\strong{Schema:}

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{
  "serverName": "jgd-http-server",
  "socketPath": "tcp://127.0.0.1:9000",
  "pid": 12345,
  "serverInfo": \{
    "httpUrl": "http://127.0.0.1:8080/"
  \}
\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{serverName}} (string, required): Human-readable server
name.
\item \strong{\code{socketPath}} (string, required): Socket URI where the
client should connect.
\item \strong{\code{pid}} (integer, required): Process ID of the server.
\item \strong{\code{serverInfo}} (object, optional): Flat key-value pairs with
string values. Canonical key: \code{httpUrl} (HTTP endpoint URL).
}

\strong{Lifecycle:}
\itemize{
\item Written atomically (temp file + rename) after all listeners
are ready.
\item Server should remove the file on graceful shutdown, but only
after confirming it still owns the file (PID check).
\item Clients should verify liveness (e.g., PID check) before using
stale files, since \verb{~/.cache} is not cleared on reboot.
\item Multiple server instances may coexist; the last writer wins.
\item Server implementors may omit discovery file support entirely.
Clients can always connect directly via an explicit socket
URI.
\item The discovery file has no explicit version field. Readers
should ignore unknown fields for forward compatibility.
}
}

\section{server_info message}{


\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{
  "type": "server_info",
  "serverName": "jgd-http-server",
  "protocolVersion": 1,
  "transport": "unix",
  "serverInfo": \{
    "httpUrl": "http://127.0.0.1:8080/"
  \}
\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{type}}: \code{"server_info"} (string, always present)
\item \strong{\code{serverName}}: Human-readable server name (string)
\item \strong{\code{protocolVersion}}: Protocol version number, currently \code{1}
(integer). Receivers should ignore messages with an unknown
protocol version rather than raising an error.
\item \strong{\code{transport}}: Transport in use: \code{"tcp"}, \code{"unix"}, or
\code{"npipe"} (string)
\item \strong{\code{serverInfo}}: A flat JSON object whose values are all
strings (optional). Canonical key: \code{httpUrl}.
}

See \code{\link[=jgd_server_info]{jgd_server_info()}} for how the R client represents this
data.
}

\section{R-to-server messages}{


\strong{ping} -- Heartbeat; triggers the deferred welcome on first
send.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"type": "ping"\}
}\if{html}{\out{</div>}}

\strong{frame} -- A complete or incremental set of drawing operations.
See the Frame message section for the full schema.

\strong{metrics_request} -- Requests font metrics from the renderer.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"type": "metrics_request", "id": 1, "kind": "strWidth",
 "str": "Hello",
 "gc": \{"font": \{"family": "sans", "face": 1,
                  "size": 12\}\}\}
}\if{html}{\out{</div>}}

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"type": "metrics_request", "id": 2, "kind": "metricInfo",
 "c": 77,
 "gc": \{"font": \{"family": "sans", "face": 1,
                  "size": 12\}\}\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{id}}: Request identifier (integer); the response must echo
it.
\item \strong{\code{kind}}: \code{"strWidth"} (string width) or \code{"metricInfo"}
(glyph metrics).
\item \strong{\code{str}} (string, \code{strWidth} only): The string to measure.
\item \strong{\code{c}} (integer, \code{metricInfo} only): Unicode code point of
the character to measure (e.g., 77 for \code{"M"}).
\item \strong{\code{gc}}: Graphics context with a \code{font} object containing
\code{family} (string), \code{face} (integer), and \code{size} (font size
in points).
}

\strong{close} -- Signals device shutdown.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"type": "close"\}
}\if{html}{\out{</div>}}
}

\section{Server-to-R messages}{


\strong{server_info} -- Welcome message (see above).

\strong{resize} -- Renderer viewport change.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"type": "resize", "width": 800, "height": 600\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{width}}, \strong{\code{height}}: New viewport dimensions in device
pixels (positive integers). These become the device's width
and height directly (no DPI scaling is applied).
\item \strong{\code{plotIndex}} (integer, optional): If present, replay the
historical plot identified by its R-assigned plot number (the
\code{plotNumber} from earlier frames) instead of the current plot.
}

\strong{metrics_response} -- Font metrics from the renderer.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"type": "metrics_response", "id": 1, "width": 48.5,
 "ascent": 10.2, "descent": 2.8\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{id}}: Must match the request \code{id}.
\item Servers should respond promptly. Clients handle their own
timeouts and may fall back to local computation. Servers are
not required to synthesize fallback responses.
}
}

\section{Frame message}{


The frame message carries drawing operations from R to the
server.

New plot example:

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{
  "type": "frame",
  "incremental": false,
  "newPage": true,
  "plotNumber": 0,
  "ext": \{\},
  "plot": \{
    "version": 1,
    "sessionId": "r-1234-1",
    "device": \{
      "width": 768,
      "height": 576,
      "dpi": 96,
      "bg": "rgba(255,255,255,1)"
    \},
    "ops": []
  \}
\}
}\if{html}{\out{</div>}}

(Minimal example; real frames typically start with a \code{clip} op.)

Historical resize replay example:

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{
  "type": "frame",
  "incremental": false,
  "resizeReplay": true,
  "plotIndex": 0,
  "plot": \{ "..." \}
\}
}\if{html}{\out{</div>}}

\strong{Top-level fields:}
\itemize{
\item \strong{\code{type}}: \code{"frame"} (always present).
\item \strong{\code{incremental}} (boolean, always present): If \code{true}, \code{ops}
contains only operations added since the last flush (delta).
If \code{false}, \code{ops} contains the complete drawing for the page.
\item \strong{\code{newPage}} (boolean, optional): Present and \code{true} when
this is a fresh plot (not a delta, not a resize replay).
\item \strong{\code{resizeReplay}} (boolean, optional): Present and \code{true}
when this frame is a replay triggered by a resize.
\item \strong{\code{plotIndex}} (integer, optional): Present during
\code{resizeReplay} when a historical plot (not the current one)
was replayed. This is the absolute R-side plot number (the
same 0-based value previously sent as \code{plotNumber} when the
plot was created). It may diverge from the renderer's current
history array index after deletions or evictions. See also
the Resize protocol section for the full resize flow.
\item \strong{\code{plotNumber}} (integer, optional): Absolute 0-based
sequence number for plots (e.g., 0 for the first, 1 for the
second). Present on all frames for the current plot (including
incremental and resize replay frames). Omitted only on
historical resize replays where \code{plotIndex} is present.
\item \strong{\code{ext}} (object, optional): Frame-level extension data.
When unset, the field is omitted (never sent as \code{null}); when
set, it may be any JSON object including an empty \code{{}}.
Servers should preserve and forward it to renderers.
}

\strong{plot object:}
\itemize{
\item \strong{\code{version}} (integer): Protocol version, currently \code{1}.
\item \strong{\code{sessionId}} (string): Identifies the R session/device.
Used for routing resize requests to the correct R process.
\item \strong{\code{device}} (object):
\itemize{
\item \strong{\code{width}}, \strong{\code{height}}: Device dimensions in pixels.
\item \strong{\code{dpi}}: Dots per inch.
\item \strong{\code{bg}}: Background color as an RGBA string
(see the Color format section).
}
\item \strong{\code{ops}} (array): Drawing operations
(see the Drawing operations section).
}
}

\section{Color format}{


Colors are represented as CSS-style RGBA strings:

\if{html}{\out{<div class="sourceCode">}}\preformatted{"rgba(R,G,B,A)"
}\if{html}{\out{</div>}}
\itemize{
\item R, G, B: integers 0--255.
\item A: decimal 0.0--1.0 (e.g., \code{"rgba(0,0,0,0.502)"}).
}

Transparent or \code{NA} colors are represented as JSON \code{null}.
}

\section{Graphics context}{


Most drawing operations include a \code{"gc"} object (exceptions are
noted per operation):

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{
  "col": "rgba(0,0,0,1)",
  "fill": null,
  "lwd": 1.0,
  "lty": [],
  "lend": "round",
  "ljoin": "round",
  "lmitre": 10.0,
  "font": \{
    "family": "sans",
    "face": 1,
    "size": 12.0,
    "lineheight": 1.2
  \},
  "ext": \{\}
\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{col}}: Stroke color (RGBA string or \code{null}).
\item \strong{\code{fill}}: Fill color (RGBA string or \code{null}).
\item \strong{\code{lwd}}: Line width in pixels (number).
\item \strong{\code{lty}}: Line type as an array of dash lengths (in pixels).
Solid lines and blank (invisible) lines both produce an empty
array \verb{[]}. When the line is blank, \code{col} is \code{null}, so
renderers can distinguish via the color.
\item \strong{\code{lend}}: Line end cap: \code{"round"}, \code{"butt"}, or \code{"square"}.
\item \strong{\code{ljoin}}: Line join: \code{"round"}, \code{"miter"}, or \code{"bevel"}.
\item \strong{\code{lmitre}}: Miter limit (number).
\item \strong{\code{font}}:
\itemize{
\item \strong{\code{family}}: Font family name (string; empty string if
default).
\item \strong{\code{face}}: Font face: 1 = plain, 2 = bold, 3 = italic,
4 = bold italic, 5 = symbol.
\item \strong{\code{size}}: Font size in points (number).
\item \strong{\code{lineheight}}: Line height multiplier (number).
}
\item \strong{\code{ext}} (object, optional): Per-operation extension data.
Present only when set. Free-form JSON.
}
}

\section{Drawing operations}{


Each element of the \code{ops} array is a JSON object with an \code{"op"}
field. Most drawing operations include a \code{"gc"} field (see
the Graphics context section). Exceptions are noted per operation.

All coordinates are in device pixels with a top-left origin (see
the Coordinate system section).

\strong{clip} -- Set the clipping rectangle. No \code{gc}.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "clip", "x0": 0, "y0": 0, "x1": 768, "y1": 576\}
}\if{html}{\out{</div>}}

\strong{line} -- A single line segment.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "line", "x1": 100, "y1": 200,
 "x2": 300, "y2": 400, "gc": \{\}\}
}\if{html}{\out{</div>}}

\strong{polyline} -- Connected line segments (not closed).

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "polyline", "x": [1, 2, 3],
 "y": [4, 5, 6], "gc": \{\}\}
}\if{html}{\out{</div>}}

\strong{polygon} -- Closed polygon (filled and/or stroked).

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "polygon", "x": [1, 2, 3],
 "y": [4, 5, 6], "gc": \{\}\}
}\if{html}{\out{</div>}}

\strong{rect} -- Rectangle.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "rect", "x0": 10, "y0": 20,
 "x1": 100, "y1": 80, "gc": \{\}\}
}\if{html}{\out{</div>}}

\strong{circle} -- Circle.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "circle", "x": 50, "y": 50, "r": 25, "gc": \{\}\}
}\if{html}{\out{</div>}}

\strong{text} -- Text string.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "text", "x": 100, "y": 200, "str": "Hello",
 "rot": 0, "hadj": 0.5, "gc": \{\}\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{str}}: The text content (string).
\item \strong{\code{rot}}: Rotation angle in degrees (counter-clockwise).
\item \strong{\code{hadj}}: Horizontal adjustment (0 = left-aligned,
0.5 = centered, 1 = right-aligned).
}

\strong{path} -- Complex path with subpaths and a fill rule.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "path", "winding": "nonzero",
 "subpaths": [[[10, 20], [30, 40], [50, 20]]],
 "gc": \{\}\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{winding}}: Fill rule: \code{"nonzero"} or \code{"evenodd"}.
\item \strong{\code{subpaths}}: Array of subpaths. Each subpath is an array
of \verb{[x, y]} coordinate pairs.
}

\strong{raster} -- Raster image. No \code{gc}.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "raster", "x": 0, "y": 576, "w": 100, "h": -80,
 "rot": 0, "interpolate": true,
 "pw": 200, "ph": 160,
 "data": "data:image/png;base64,..."\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{x}}, \strong{\code{y}}: Bottom-left corner of the destination
rectangle in device coordinates.
\item \strong{\code{w}}, \strong{\code{h}}: Displayed width and height. May be
\strong{negative} to indicate a horizontal or vertical flip;
renderers should use the absolute value for sizing and adjust
the anchor point accordingly.
\item \strong{\code{pw}}, \strong{\code{ph}}: Pixel width and height of the source
image.
\item \strong{\code{rot}}: Rotation angle in degrees.
\item \strong{\code{interpolate}}: Whether to interpolate when scaling.
\item \strong{\code{data}}: Base64-encoded PNG as a data URI.
}

\strong{beginGroup} -- Start a drawing group (experimental). No \code{gc}.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "beginGroup",
 "ext": \{"filter": "blur(5px)", "opacity": 0.8\}\}
}\if{html}{\out{</div>}}
\itemize{
\item \strong{\code{ext}} (object, optional): Group-level extension data.
Present only when set. Free-form JSON. Common keys: \code{filter}
(CSS filter string), \code{opacity} (number 0--1), \code{blendMode}
(CSS blend mode string).
}

\strong{endGroup} -- End the most recently opened group. No \code{gc}, no
fields other than \code{"op"}.

\if{html}{\out{<div class="sourceCode json">}}\preformatted{\{"op": "endGroup"\}
}\if{html}{\out{</div>}}

Groups nest arbitrarily.
}

\section{Resize protocol}{


The server receives resize messages from the renderer and
forwards them to R. R replays the drawing at the new dimensions
and sends back a frame message.

\strong{Normal resize flow:}

\if{html}{\out{<div class="sourceCode">}}\preformatted{Renderer -> Server:  \{"type":"resize","width":800,
                      "height":600\}
Server   -> R:       \{"type":"resize","width":800,
                      "height":600\}
R        -> Server:  \{"type":"frame",
                      "resizeReplay":true,
                      "incremental":false,...\}
}\if{html}{\out{</div>}}

\strong{History resize flow} (replay a historical plot):

\if{html}{\out{<div class="sourceCode">}}\preformatted{Renderer -> Server:  \{"type":"resize","width":800,
                      "height":600,"plotIndex":2,
                      "sessionId":"r-1234-1"\}
Server   -> R:       \{"type":"resize","width":800,
                      "height":600,"plotIndex":2\}
R        -> Server:  \{"type":"frame",
                      "resizeReplay":true,
                      "plotIndex":2,
                      "incremental":false,...\}
}\if{html}{\out{</div>}}

Note: The server strips \code{sessionId} before forwarding to R.
History resizes are routed only to the R session that owns the
target plot.

\strong{Resize deduplication:}

Servers should deduplicate consecutive normal resizes with
identical dimensions for each R session. However, if the
previous resize was a \code{plotIndex} resize, the next normal resize
at the same dimensions must NOT be deduplicated, because they
target different contexts (historical snapshot vs. current
plot).
}

\section{Multiple R sessions}{


A server may accept connections from multiple R processes
simultaneously. Each R connection has its own \code{sessionId} and
independent state. Servers should:
\itemize{
\item Route \code{metrics_request} messages from each R connection to
the renderer, and route the matching \code{metrics_response} back
to the originating R session. The \code{id} field is only unique
within a single R process, so servers must scope routing by
the originating connection (e.g. keyed by session + \code{id}).
If a server remaps IDs when forwarding to a shared renderer,
it must restore the original \code{id} when relaying the response
back to R.
\item Route \code{plotIndex} resizes to the R session that owns the
target plot (identified by \code{sessionId} in the resize message).
\item Broadcast normal resizes to all connected R sessions.
\item Broadcast \code{frame} and \code{close} messages to all connected
renderers.
}
}

\section{Session ID management}{


The \code{sessionId} in frame messages identifies the R device
instance. Servers should treat it as an \strong{opaque string}. Do
not parse it or make assumptions about its format.

Session ID reuse is unlikely but possible (e.g., after process
restart). As a defensive measure, servers should disambiguate
if a previously retired \code{sessionId} reappears, to prevent
\code{plotIndex} resizes for old plots from reaching the new
connection. The server's (possibly remapped) \code{sessionId} is
what the renderer sees; \code{plotIndex} resizes use it for routing.
}

\section{Connection lifecycle}{


\strong{Graceful close:} The client sends \code{{"type":"close"}} on
device shutdown. The server should forward this to renderers
and clean up routing state for that session.

\strong{Ungraceful disconnect:} If the connection drops without a
\code{close} message (e.g., process crash), the server should detect
the broken connection (EOF or socket error), clean up the
session, and optionally notify renderers.

\strong{Incomplete lines:} If a connection drops mid-line (no
trailing \verb{\\n}), the partial data should be discarded.
}

\section{Extension fields}{


Extension fields (\code{ext}) appear at three levels:
\itemize{
\item \strong{Frame-level}: Top-level \code{ext} on the frame message.
Applies to the entire frame.
\item \strong{Graphics context level}: \code{ext} inside the \code{gc} object.
Applies to all drawing operations while active.
\item \strong{Group level}: \code{ext} on \code{beginGroup} operations. Applies
only to that group.
}

All \code{ext} fields are free-form JSON objects. When unset, the
field is omitted from the message (never sent as \code{null}). When
set, it may contain any JSON object, including an empty \code{{}}.
Servers should preserve and forward them to renderers without
validation. Renderers should ignore unknown keys.

Extension fields are preserved across resize replays, so
historical plot snapshots retain their \code{ext} data.

See \code{\link[=jgd_ext]{jgd_ext()}}, \code{\link[=jgd_frame_ext]{jgd_frame_ext()}}, and \code{\link[=jgd_begin_group]{jgd_begin_group()}}
for the R API to set these fields.
}

\section{Implementing a server}{


A minimal server implementation needs to:
\enumerate{
\item Listen on a Unix socket, named pipe, or TCP port.
\item Accept R connections and read JSONL lines.
\item Send a deferred \code{server_info} welcome after receiving the
first message from R (not before).
\item Forward \code{frame} messages to connected renderers.
\item Forward \code{resize} messages from renderers to R.
\item Handle \code{metrics_request}/\code{metrics_response} routing between
R and the renderer. Clients handle their own timeouts;
servers are not required to synthesize fallback responses.
\item Forward \code{close} messages to renderers and clean up the
session state (remove metrics routing entries, etc.).
}

Optional:
\itemize{
\item Write a discovery file on startup, remove on shutdown.
\item Implement resize deduplication.
\item Implement session ID remapping for reused IDs.
\item Serve an HTTP endpoint for the renderer UI.
}
}

\seealso{
\code{\link[=jgd]{jgd()}}, \code{\link[=jgd_server_info]{jgd_server_info()}}, \code{\link[=jgd_ext]{jgd_ext()}},
\code{\link[=jgd_frame_ext]{jgd_frame_ext()}}, \code{\link[=jgd_begin_group]{jgd_begin_group()}}
}
