<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blockyard</title><link>https://cynkra.github.io/blockyard/</link><description>Recent content on Blockyard</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://cynkra.github.io/blockyard/index.xml" rel="self" type="application/rss+xml"/><item><title>CLI Reference</title><link>https://cynkra.github.io/blockyard/docs/reference/cli/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/reference/cli/</guid><description>&lt;p>&lt;code>by&lt;/code> is the command-line client for Blockyard. It handles authentication,
deployment, app management, and administration from your terminal.&lt;/p>
&lt;h2 id="global-flags">Global flags&lt;a class="anchor" href="#global-flags">#&lt;/a>&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Flag&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>--json&lt;/code>&lt;/td>
 &lt;td>Output machine-readable JSON (all commands)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>When &lt;code>--json&lt;/code> is set, all output (including errors) is printed as
pretty-printed JSON. Errors use the shape &lt;code>{&amp;quot;error&amp;quot;: &amp;quot;...&amp;quot;, &amp;quot;message&amp;quot;: &amp;quot;...&amp;quot;}&lt;/code>.&lt;/p>
&lt;h2 id="authentication">Authentication&lt;a class="anchor" href="#authentication">#&lt;/a>&lt;/h2>
&lt;h3 id="by-login">&lt;code>by login&lt;/code>&lt;a class="anchor" href="#by-login">#&lt;/a>&lt;/h3>
&lt;p>Store credentials interactively. Opens a browser to create a
&lt;a href="https://cynkra.github.io/blockyard/docs/guides/authorization/#personal-access-tokens">Personal Access Token&lt;/a>, then
verifies it against the server.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">by login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">by login --server https://blockyard.example.com&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Flag&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>--server &amp;lt;url&amp;gt;&lt;/code>&lt;/td>
 &lt;td>Server URL (prompts interactively if omitted)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Credentials are saved to &lt;code>~/.config/by/config.json&lt;/code> (respects
&lt;code>$XDG_CONFIG_HOME&lt;/code>) with file mode &lt;code>0600&lt;/code>.&lt;/p></description></item><item><title>Deploying an App</title><link>https://cynkra.github.io/blockyard/docs/guides/deploying/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/guides/deploying/</guid><description>&lt;h2 id="bundle-structure">Bundle structure&lt;a class="anchor" href="#bundle-structure">#&lt;/a>&lt;/h2>
&lt;p>A bundle is a &lt;code>.tar.gz&lt;/code> archive containing your Shiny app source. At
minimum it needs:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>app.R&lt;/code>&lt;/strong> (or &lt;code>ui.R&lt;/code> + &lt;code>server.R&lt;/code>) — the Shiny application code&lt;/li>
&lt;/ul>
&lt;p>Optionally include dependency metadata in one of these formats (highest
priority first): &lt;code>manifest.json&lt;/code>, &lt;code>renv.lock&lt;/code>, or &lt;code>DESCRIPTION&lt;/code>. If none
is present, Blockyard scans your scripts to discover dependencies
automatically.&lt;/p>
&lt;p>Any additional files (data, assets, R scripts sourced by the app) are
included automatically when you tar the directory.&lt;/p></description></item><item><title>What is Blockyard?</title><link>https://cynkra.github.io/blockyard/docs/getting-started/overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/getting-started/overview/</guid><description>&lt;p>Blockyard is a self-hosted platform for running
&lt;a href="https://github.com/blockr-org/blockr">blockr&lt;/a> applications in production.
blockr apps evaluate user-supplied R expressions inside every session,
which is a security model no general-purpose
&lt;a href="https://shiny.posit.co/">Shiny&lt;/a> host was designed for. Blockyard provides
hardened container-per-session isolation, per-user credential management
via &lt;a href="https://openbao.org/">OpenBao&lt;/a>, server-side dependency resolution,
and built-in board storage — the full blockr stack as a single binary.&lt;/p>
&lt;h2 id="how-it-works">How it works&lt;a class="anchor" href="#how-it-works">#&lt;/a>&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>You deploy a bundle&lt;/strong> — run &lt;code>by deploy ./my-board&lt;/code> or upload a
&lt;code>.tar.gz&lt;/code> archive via the REST API. Bundles can include dependency
metadata (&lt;code>renv.lock&lt;/code>, &lt;code>DESCRIPTION&lt;/code>, or &lt;code>manifest.json&lt;/code>), or none at
all — the server discovers what&amp;rsquo;s needed.&lt;/li>
&lt;li>&lt;strong>Blockyard resolves dependencies server-side&lt;/strong> — it spins up a build
container and uses &lt;a href="https://pak.r-lib.org/">pak&lt;/a> to install packages.
Unpinned bundles can be refreshed in place later without a redeploy.&lt;/li>
&lt;li>&lt;strong>Users visit the app&lt;/strong> — when a request hits &lt;code>/app/&amp;lt;name&amp;gt;/&lt;/code>, Blockyard
spawns a worker container on demand and reverse-proxies HTTP and
WebSocket traffic to it. Each session gets its own container.&lt;/li>
&lt;/ol>
&lt;p>Workers are isolated from each other via per-container bridge networks.
Containers run with a read-only filesystem, all Linux capabilities dropped,
&lt;code>no-new-privileges&lt;/code> set, and cloud metadata endpoint access blocked
(&lt;code>169.254.169.254&lt;/code>). For high-security deployments, workers can run under
the &lt;a href="https://katacontainers.io/">Kata&lt;/a> runtime, which gives each session
its own lightweight VM. See
&lt;a href="https://cynkra.github.io/blockyard/docs/guides/deploying/#container-security">Deploying an App&lt;/a>
for the full list of security settings.&lt;/p></description></item><item><title>Configuration</title><link>https://cynkra.github.io/blockyard/docs/guides/configuration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/guides/configuration/</guid><description>&lt;p>Blockyard is configured via a TOML file. By default, it looks for
&lt;code>blockyard.toml&lt;/code> in the current directory. Override the path with the
&lt;code>--config&lt;/code> CLI argument:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">blockyard --config /etc/blockyard/config.toml&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Every config field can also be overridden via environment variables using the
pattern &lt;code>BLOCKYARD_&amp;lt;SECTION&amp;gt;_&amp;lt;FIELD&amp;gt;&lt;/code> (uppercased). For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BLOCKYARD_SERVER_BIND&lt;/span>&lt;span class="o">=&lt;/span>0.0.0.0:9090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BLOCKYARD_DOCKER_IMAGE&lt;/span>&lt;span class="o">=&lt;/span>ghcr.io/rocker-org/r-ver:4.4.0&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="sections">Sections&lt;a class="anchor" href="#sections">#&lt;/a>&lt;/h2>
&lt;h3 id="server">&lt;code>[server]&lt;/code>&lt;a class="anchor" href="#server">#&lt;/a>&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Field&lt;/th>
 &lt;th>Default&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>bind&lt;/code>&lt;/td>
 &lt;td>&lt;code>127.0.0.1:8080&lt;/code>&lt;/td>
 &lt;td>Address and port the server listens on&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>shutdown_timeout&lt;/code>&lt;/td>
 &lt;td>&lt;code>30s&lt;/code>&lt;/td>
 &lt;td>Time to drain in-flight requests on shutdown&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>log_level&lt;/code>&lt;/td>
 &lt;td>&lt;code>info&lt;/code>&lt;/td>
 &lt;td>Log verbosity: &lt;code>trace&lt;/code>, &lt;code>debug&lt;/code>, &lt;code>info&lt;/code>, &lt;code>warn&lt;/code>, &lt;code>error&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>management_bind&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>Separate listener for &lt;code>/healthz&lt;/code>, &lt;code>/readyz&lt;/code>, &lt;code>/metrics&lt;/code>. See &lt;a href="https://cynkra.github.io/blockyard/docs/guides/observability/#management-listener">Observability&lt;/a>.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>session_secret&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>Secret for signing session cookies. Required when &lt;code>[oidc]&lt;/code> is set without &lt;code>[openbao]&lt;/code>; auto-generated and stored in vault when &lt;code>[openbao]&lt;/code> is configured.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>external_url&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>Public-facing URL of the server (used for OIDC redirect URIs)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>trusted_proxies&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>CIDRs whose &lt;code>X-Forwarded-For&lt;/code> to trust (e.g. &lt;code>[&amp;quot;10.0.0.0/8&amp;quot;]&lt;/code>)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="docker">&lt;code>[docker]&lt;/code>&lt;a class="anchor" href="#docker">#&lt;/a>&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Field&lt;/th>
 &lt;th>Default&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>socket&lt;/code>&lt;/td>
 &lt;td>&lt;code>/var/run/docker.sock&lt;/code>&lt;/td>
 &lt;td>Path to the Docker (or Podman) socket&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>image&lt;/code>&lt;/td>
 &lt;td>&lt;em>(required)&lt;/em>&lt;/td>
 &lt;td>Base container image for workers and builds&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>shiny_port&lt;/code>&lt;/td>
 &lt;td>&lt;code>3838&lt;/code>&lt;/td>
 &lt;td>Port Shiny listens on inside the container&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>pak_version&lt;/code>&lt;/td>
 &lt;td>&lt;code>stable&lt;/code>&lt;/td>
 &lt;td>&lt;a href="https://pak.r-lib.org/">pak&lt;/a> release channel (&lt;code>stable&lt;/code>, &lt;code>rc&lt;/code>, or &lt;code>devel&lt;/code>)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>service_network&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>Docker network whose containers are reachable from workers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>store_retention&lt;/code>&lt;/td>
 &lt;td>&lt;code>0&lt;/code>&lt;/td>
 &lt;td>Package store eviction duration (&lt;code>0&lt;/code> = disabled)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>default_memory_limit&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>Fallback memory limit for workers (e.g. &lt;code>&amp;quot;2g&amp;quot;&lt;/code>). Applies when no per-app limit is set.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>default_cpu_limit&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>Fallback CPU limit for workers (e.g. &lt;code>4.0&lt;/code>). Applies when no per-app limit is set.&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="storage">&lt;code>[storage]&lt;/code>&lt;a class="anchor" href="#storage">#&lt;/a>&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Field&lt;/th>
 &lt;th>Default&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>bundle_server_path&lt;/code>&lt;/td>
 &lt;td>&lt;code>/data/bundles&lt;/code>&lt;/td>
 &lt;td>Directory for uploaded bundles&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>bundle_worker_path&lt;/code>&lt;/td>
 &lt;td>&lt;code>/app&lt;/code>&lt;/td>
 &lt;td>Mount point inside worker containers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>bundle_retention&lt;/code>&lt;/td>
 &lt;td>&lt;code>50&lt;/code>&lt;/td>
 &lt;td>Max bundles to keep per app before cleanup&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>max_bundle_size&lt;/code>&lt;/td>
 &lt;td>&lt;code>104857600&lt;/code>&lt;/td>
 &lt;td>Maximum upload size in bytes (default 100 MB)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>soft_delete_retention&lt;/code>&lt;/td>
 &lt;td>&lt;code>0&lt;/code>&lt;/td>
 &lt;td>How long to keep soft-deleted apps (e.g. &lt;code>720h&lt;/code> for 30 days). &lt;code>0&lt;/code> means immediate hard delete.&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="database">&lt;code>[database]&lt;/code>&lt;a class="anchor" href="#database">#&lt;/a>&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Field&lt;/th>
 &lt;th>Default&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>driver&lt;/code>&lt;/td>
 &lt;td>&lt;code>sqlite&lt;/code>&lt;/td>
 &lt;td>Database driver: &lt;code>sqlite&lt;/code> or &lt;code>postgres&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>path&lt;/code>&lt;/td>
 &lt;td>&lt;code>/data/db/blockyard.db&lt;/code>&lt;/td>
 &lt;td>Path to the SQLite database file (when &lt;code>driver = &amp;quot;sqlite&amp;quot;&lt;/code>)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>url&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>PostgreSQL connection string (when &lt;code>driver = &amp;quot;postgres&amp;quot;&lt;/code>)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="proxy">&lt;code>[proxy]&lt;/code>&lt;a class="anchor" href="#proxy">#&lt;/a>&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Field&lt;/th>
 &lt;th>Default&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>ws_cache_ttl&lt;/code>&lt;/td>
 &lt;td>&lt;code>60s&lt;/code>&lt;/td>
 &lt;td>How long to keep a backend WebSocket open after client disconnect. Enables transparent reconnection on network blips — see &lt;a href="https://cynkra.github.io/blockyard/docs/guides/deploying/#reconnection-on-network-interruptions">Reconnection on network interruptions&lt;/a>.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>health_interval&lt;/code>&lt;/td>
 &lt;td>&lt;code>15s&lt;/code>&lt;/td>
 &lt;td>Interval between worker health checks&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>worker_start_timeout&lt;/code>&lt;/td>
 &lt;td>&lt;code>60s&lt;/code>&lt;/td>
 &lt;td>Max time to wait for a worker to become healthy&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>max_workers&lt;/code>&lt;/td>
 &lt;td>&lt;code>100&lt;/code>&lt;/td>
 &lt;td>Maximum number of concurrent worker containers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>log_retention&lt;/code>&lt;/td>
 &lt;td>&lt;code>1h&lt;/code>&lt;/td>
 &lt;td>How long to keep worker log entries before cleanup&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>session_idle_ttl&lt;/code>&lt;/td>
 &lt;td>&lt;code>0&lt;/code>&lt;/td>
 &lt;td>Idle timeout for sessions and WebSocket connections. When non-zero, idle WebSocket connections are closed and stale sessions are swept. &lt;code>0&lt;/code> = disabled.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>idle_worker_timeout&lt;/code>&lt;/td>
 &lt;td>&lt;code>5m&lt;/code>&lt;/td>
 &lt;td>Time before an idle worker container is stopped&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>http_forward_timeout&lt;/code>&lt;/td>
 &lt;td>&lt;code>5m&lt;/code>&lt;/td>
 &lt;td>Timeout for forwarding HTTP requests to workers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>max_cpu_limit&lt;/code>&lt;/td>
 &lt;td>&lt;code>16.0&lt;/code>&lt;/td>
 &lt;td>Max CPU limit settable per app&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>transfer_timeout&lt;/code>&lt;/td>
 &lt;td>&lt;code>60s&lt;/code>&lt;/td>
 &lt;td>Timeout for bundle transfers to workers&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>session_max_lifetime&lt;/code>&lt;/td>
 &lt;td>&lt;code>0&lt;/code>&lt;/td>
 &lt;td>Hard cap on session duration. &lt;code>0&lt;/code> (default) means unlimited — sessions only end via idle timeout or worker shutdown.&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="oidc-optional">&lt;code>[oidc]&lt;/code> &lt;em>(optional)&lt;/em>&lt;a class="anchor" href="#oidc-optional">#&lt;/a>&lt;/h3>
&lt;p>Enable OIDC authentication. When configured, &lt;code>server.session_secret&lt;/code> is required unless &lt;code>[openbao]&lt;/code> is also configured (in which case it can be auto-generated).&lt;/p></description></item><item><title>Installation</title><link>https://cynkra.github.io/blockyard/docs/getting-started/installation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/getting-started/installation/</guid><description>&lt;h2 id="server">Server&lt;a class="anchor" href="#server">#&lt;/a>&lt;/h2>
&lt;h3 id="prerequisites">Prerequisites&lt;a class="anchor" href="#prerequisites">#&lt;/a>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Docker&lt;/strong> (or Podman with a Docker-compatible socket)&lt;/li>
&lt;li>A Linux host (Blockyard runs as a container or native binary)&lt;/li>
&lt;/ul>
&lt;h3 id="running-with-docker-recommended">Running with Docker (recommended)&lt;a class="anchor" href="#running-with-docker-recommended">#&lt;/a>&lt;/h3>
&lt;p>The easiest way to run Blockyard is as a Docker container with access to the
host&amp;rsquo;s Docker socket:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --name blockyard &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -p 8080:8080 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -v /var/run/docker.sock:/var/run/docker.sock &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -v blockyard-data:/data &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -e &lt;span class="nv">BLOCKYARD_DOCKER_IMAGE&lt;/span>&lt;span class="o">=&lt;/span>ghcr.io/rocker-org/r-ver:4.4.3 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> ghcr.io/cynkra/blockyard:latest&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This gives Blockyard access to Docker for spawning worker containers, and
persists application data (bundles, database) in a named volume.&lt;/p></description></item><item><title>REST API</title><link>https://cynkra.github.io/blockyard/docs/reference/api/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/reference/api/</guid><description>&lt;p>All endpoints are under &lt;code>/api/v1/&lt;/code> and require authentication (except
&lt;code>/healthz&lt;/code>, &lt;code>/readyz&lt;/code>, and &lt;code>/metrics&lt;/code>).&lt;/p>
&lt;p>Authenticate with a &lt;a href="https://cynkra.github.io/blockyard/docs/guides/authorization/#personal-access-tokens">Personal Access Token&lt;/a>
(&lt;code>Authorization: Bearer by_...&lt;/code>) or an OIDC session cookie (browser).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -H &lt;span class="s2">&amp;#34;Authorization: Bearer &lt;/span>&lt;span class="nv">$TOKEN&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> ...&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="health">Health&lt;a class="anchor" href="#health">#&lt;/a>&lt;/h2>
&lt;h3 id="get-healthz">&lt;code>GET /healthz&lt;/code>&lt;a class="anchor" href="#get-healthz">#&lt;/a>&lt;/h3>
&lt;p>Returns &lt;code>200 OK&lt;/code> with body &lt;code>ok&lt;/code>. No authentication required.&lt;/p>
&lt;h3 id="get-readyz">&lt;code>GET /readyz&lt;/code>&lt;a class="anchor" href="#get-readyz">#&lt;/a>&lt;/h3>
&lt;p>Readiness probe that checks backend dependencies (database, Docker socket, and
optionally IdP and OpenBao). No authentication required, but the response
detail varies based on the caller.&lt;/p></description></item><item><title>Authorization</title><link>https://cynkra.github.io/blockyard/docs/guides/authorization/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/guides/authorization/</guid><description>&lt;p>Blockyard separates authentication from authorization. Your identity
provider (IdP) handles authentication — proving who you are via OIDC.
Blockyard handles authorization — deciding what you can do. IdP groups
play no role in blockyard&amp;rsquo;s permission model.&lt;/p>
&lt;h2 id="system-roles">System Roles&lt;a class="anchor" href="#system-roles">#&lt;/a>&lt;/h2>
&lt;p>Every user has one system-wide role, assigned by a blockyard admin.
New users get &lt;strong>viewer&lt;/strong> by default when they first log in via OIDC.&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Role&lt;/th>
 &lt;th>What you can do&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>admin&lt;/strong>&lt;/td>
 &lt;td>Full control: manage all apps, all users, all settings. Implicit owner access to everything.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>publisher&lt;/strong>&lt;/td>
 &lt;td>Create and deploy apps. Full control over your own apps. No access to other publishers&amp;rsquo; apps unless explicitly granted.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>viewer&lt;/strong>&lt;/td>
 &lt;td>Access apps you&amp;rsquo;ve been granted access to, or apps with &lt;code>logged_in&lt;/code>/&lt;code>public&lt;/code> visibility. Cannot create or deploy apps.&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="first-admin-setup">First Admin Setup&lt;a class="anchor" href="#first-admin-setup">#&lt;/a>&lt;/h3>
&lt;p>The first admin is configured via the &lt;code>initial_admin&lt;/code> field in the
&lt;code>[oidc]&lt;/code> config section. Set this to the OIDC &lt;code>sub&lt;/code> of the user who
should become the first admin:&lt;/p></description></item><item><title>Configuration File</title><link>https://cynkra.github.io/blockyard/docs/reference/config/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/reference/config/</guid><description>&lt;p>Blockyard reads its configuration from a TOML file. The default path is
&lt;code>blockyard.toml&lt;/code> in the working directory. Override it with the
&lt;code>--config&lt;/code> CLI argument:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">blockyard --config /etc/blockyard/config.toml&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="environment-variable-overrides">Environment variable overrides&lt;a class="anchor" href="#environment-variable-overrides">#&lt;/a>&lt;/h2>
&lt;p>Every configuration field can be set via an environment variable. The naming
convention is:&lt;/p>
&lt;pre tabindex="0">&lt;code>BLOCKYARD_&amp;lt;SECTION&amp;gt;_&amp;lt;FIELD&amp;gt;&lt;/code>&lt;/pre>&lt;p>All uppercased. For example, &lt;code>[server] bind&lt;/code> becomes &lt;code>BLOCKYARD_SERVER_BIND&lt;/code>.&lt;/p>
&lt;p>Environment variables take precedence over values in the TOML file.&lt;/p>
&lt;h2 id="server">&lt;code>[server]&lt;/code>&lt;a class="anchor" href="#server">#&lt;/a>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">server&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">bind&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;127.0.0.1:8080&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">shutdown_timeout&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;30s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># management_bind = &amp;#34;127.0.0.1:9100&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># log_level = &amp;#34;info&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># session_secret = &amp;#34;random-secret&amp;#34; # required when [oidc] is configured&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># external_url = &amp;#34;https://blockyard.example.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># trusted_proxies = [&amp;#34;10.0.0.0/8&amp;#34;]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Field&lt;/th>
 &lt;th>Type&lt;/th>
 &lt;th>Default&lt;/th>
 &lt;th>Required&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>bind&lt;/code>&lt;/td>
 &lt;td>&lt;code>string&lt;/code>&lt;/td>
 &lt;td>&lt;code>127.0.0.1:8080&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>Socket address to listen on&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>management_bind&lt;/code>&lt;/td>
 &lt;td>&lt;code>string&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>Separate listener for &lt;code>/healthz&lt;/code>, &lt;code>/readyz&lt;/code>, &lt;code>/metrics&lt;/code>. See &lt;a href="https://cynkra.github.io/blockyard/docs/guides/observability/#management-listener">Management listener&lt;/a>.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>shutdown_timeout&lt;/code>&lt;/td>
 &lt;td>&lt;code>duration&lt;/code>&lt;/td>
 &lt;td>&lt;code>30s&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>Grace period for draining requests on shutdown&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>log_level&lt;/code>&lt;/td>
 &lt;td>&lt;code>string&lt;/code>&lt;/td>
 &lt;td>&lt;code>info&lt;/code>&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>Log verbosity. One of &lt;code>trace&lt;/code>, &lt;code>debug&lt;/code>, &lt;code>info&lt;/code>, &lt;code>warn&lt;/code> (or &lt;code>warning&lt;/code>), &lt;code>error&lt;/code>.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>session_secret&lt;/code>&lt;/td>
 &lt;td>&lt;code>string&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>When &lt;code>[oidc]&lt;/code> is set without &lt;code>[openbao]&lt;/code>&lt;/td>
 &lt;td>Secret for signing session cookies. Supports &lt;a href="#vault-references">vault references&lt;/a>. Auto-generated and stored in vault when &lt;code>[openbao]&lt;/code> is configured.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>external_url&lt;/code>&lt;/td>
 &lt;td>&lt;code>string&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>Public-facing URL of the server (used for OIDC redirect URIs)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>trusted_proxies&lt;/code>&lt;/td>
 &lt;td>&lt;code>string[]&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>CIDRs whose &lt;code>X-Forwarded-For&lt;/code> headers to trust (e.g. &lt;code>[&amp;quot;10.0.0.0/8&amp;quot;]&lt;/code>). Each entry must be a valid CIDR. Set via env as comma-separated: &lt;code>BLOCKYARD_SERVER_TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12&lt;/code>.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>bootstrap_token&lt;/code>&lt;/td>
 &lt;td>&lt;code>string&lt;/code>&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>No&lt;/td>
 &lt;td>One-time token that can be exchanged for a real PAT via &lt;code>POST /api/v1/bootstrap&lt;/code>. Requires &lt;code>oidc.initial_admin&lt;/code> to be set. Intended for dev/CI bootstrapping — do not use in production. See &lt;a href="https://cynkra.github.io/blockyard/docs/reference/api/#post-apiv1bootstrap">Bootstrap tokens&lt;/a>.&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;blockquote class='book-hint note'>
&lt;p>API authentication uses &lt;a href="https://cynkra.github.io/blockyard/docs/guides/authorization/#personal-access-tokens">Personal Access Tokens&lt;/a>
(for CLI/CI access) or OIDC session cookies (for browser access). The v0
static bearer token (&lt;code>server.token&lt;/code>) has been removed.&lt;/p></description></item><item><title>Quick Start</title><link>https://cynkra.github.io/blockyard/docs/getting-started/quickstart/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/getting-started/quickstart/</guid><description>&lt;p>This guide walks through deploying a Shiny app using the &lt;code>by&lt;/code> CLI. It assumes
Blockyard is already running (see &lt;a href="https://cynkra.github.io/blockyard/docs/getting-started/installation/">Installation&lt;/a>).&lt;/p>
&lt;h2 id="1-install-the-cli">1. Install the CLI&lt;a class="anchor" href="#1-install-the-cli">#&lt;/a>&lt;/h2>
&lt;p>Download the &lt;code>by&lt;/code> binary for your platform from the
&lt;a href="https://github.com/cynkra/blockyard/releases">releases page&lt;/a> and place it on
your &lt;code>PATH&lt;/code>. See &lt;a href="https://cynkra.github.io/blockyard/docs/getting-started/installation/#cli">Installation&lt;/a> for details.&lt;/p>
&lt;h2 id="2-log-in">2. Log in&lt;a class="anchor" href="#2-log-in">#&lt;/a>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">by login --server http://localhost:8080&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This opens your browser to create a
&lt;a href="https://cynkra.github.io/blockyard/docs/guides/authorization/#personal-access-tokens">Personal Access Token&lt;/a>, then
asks you to paste it back into the terminal.&lt;/p>
&lt;h2 id="3-create-your-app">3. Create your app&lt;a class="anchor" href="#3-create-your-app">#&lt;/a>&lt;/h2>
&lt;p>Your app directory should look something like:&lt;/p></description></item><item><title>Credential Management</title><link>https://cynkra.github.io/blockyard/docs/guides/credentials/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/guides/credentials/</guid><description>&lt;p>Blockyard integrates with &lt;a href="https://openbao.org/">OpenBao&lt;/a> (a Vault-compatible
secrets manager) to deliver per-user credentials to Shiny apps at runtime.
This allows each user to register API keys for external services (AI providers,
databases, object storage, etc.) that are securely injected into their
sessions.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;a class="anchor" href="#prerequisites">#&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>OIDC authentication must be configured (see &lt;a href="https://cynkra.github.io/blockyard/docs/guides/configuration/">Configuration&lt;/a>)&lt;/li>
&lt;li>An OpenBao (or HashiCorp Vault) instance, initialized and unsealed&lt;/li>
&lt;/ul>
&lt;h2 id="how-it-works">How it works&lt;a class="anchor" href="#how-it-works">#&lt;/a>&lt;/h2>
&lt;ol>
&lt;li>An operator configures OpenBao and defines which services users can
enroll credentials for&lt;/li>
&lt;li>Users store their API keys via the web UI or the REST API&lt;/li>
&lt;li>When a user visits a Shiny app, Blockyard injects a scoped OpenBao token
into the request so the app can read that user&amp;rsquo;s credentials&lt;/li>
&lt;/ol>
&lt;p>No single compromised component can exfiltrate all user credentials. The
server&amp;rsquo;s OpenBao token is write-scoped — it cannot read user secrets. Only a
valid IdP access token (from an active user session) can produce a read-scoped
token.&lt;/p></description></item><item><title>Observability</title><link>https://cynkra.github.io/blockyard/docs/guides/observability/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/guides/observability/</guid><description>&lt;p>Blockyard provides four observability mechanisms: structured logging,
Prometheus metrics, OpenTelemetry tracing, and an append-only audit log.&lt;/p>
&lt;h2 id="logging">Logging&lt;a class="anchor" href="#logging">#&lt;/a>&lt;/h2>
&lt;p>Blockyard writes structured JSON logs to stderr. Control verbosity with
the &lt;code>log_level&lt;/code> setting:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">server&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">log_level&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;info&amp;#34;&lt;/span> &lt;span class="c"># trace, debug, info, warn, error&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Or via environment variable:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BLOCKYARD_SERVER_LOG_LEVEL&lt;/span>&lt;span class="o">=&lt;/span>debug&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="log-levels">Log levels&lt;a class="anchor" href="#log-levels">#&lt;/a>&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Level&lt;/th>
 &lt;th>Use case&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>trace&lt;/code>&lt;/td>
 &lt;td>Fine-grained diagnostics (WebSocket frames, load-balancer decisions). Noisy.&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>debug&lt;/code>&lt;/td>
 &lt;td>Subsystem internals (health checks, session lifecycle, container operations)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>info&lt;/code>&lt;/td>
 &lt;td>Normal operations (startup, requests, deploys). &lt;strong>Default.&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>warn&lt;/code>&lt;/td>
 &lt;td>Degraded conditions (failed health checks, capacity limits)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>error&lt;/code>&lt;/td>
 &lt;td>Failures requiring attention (container crashes, build failures)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="http-request-logging">HTTP request logging&lt;a class="anchor" href="#http-request-logging">#&lt;/a>&lt;/h3>
&lt;p>All HTTP requests are logged automatically:&lt;/p></description></item><item><title>Backend Security</title><link>https://cynkra.github.io/blockyard/docs/guides/backend-security/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cynkra.github.io/blockyard/docs/guides/backend-security/</guid><description>&lt;p>Blockyard supports two worker backends: &lt;strong>Docker&lt;/strong> (the default) and
&lt;strong>process&lt;/strong> (bubblewrap-based, single-host). Both run untrusted R code with
PID, mount, user-namespace, and capability isolation. They differ on three
axes — network isolation between workers, per-worker resource limits, and
syscall filtering — and on how much privilege the server process itself
needs. This page helps you decide which backend fits your deployment and,
if you pick the process backend, how to close the gaps with
operator-configured controls.&lt;/p></description></item></channel></rss>