Skip to content

semilayer/runner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

SemiLayer
SemiLayer / runner

The self-hosted query runner for SemiLayer. Dials out to SemiLayer, executes database queries locally, and streams results back. Your database credentials never have to leave your network.

GHCR npm docs license


TL;DR

docker run --rm \
  -e SEMILAYER_RUNNER_ID=<runner-id> \
  -e SEMILAYER_RUNNER_TOKEN=rk_... \
  ghcr.io/semilayer/runner:latest

One outbound WebSocket. No inbound ports. Queries come in over the socket; the runner opens the DB connection, runs the query, streams rows back.

Why a runner?

   ┌────────────────────────┐                ┌────────────────────────┐
   │  api.semilayer.com     │                │  your network          │
   │  console.semilayer.com │                │  ┌──────────────┐      │
   │  runner.semilayer.com  │◄───── wss ─────┤  │  runner      │      │
   │                        │     outbound   │  │  (this repo) │      │
   └────────────────────────┘      only      │  └──────┬───────┘      │
                                             │         │              │
                                             │         ▼              │
                                             │  ┌──────────────┐      │
                                             │  │  your DB     │      │
                                             │  └──────────────┘      │
                                             └────────────────────────┘
  • Zero inbound. Your database never takes a connection from SemiLayer's IPs. The runner dials out over TLS 1.3; SemiLayer never opens a port toward you.
  • Airgap-ready. In runner-local credentials mode, SemiLayer never holds your DB URL at all — only the runner knows how to connect. Rotate a password? You do it on your side; SemiLayer was never in the loop.
  • Redundant by default. Run two (or twenty) with the same assignments. Dedup is handled by an atomic claim on the gateway — no double-execution, no leader election, no coordination.
  • Drop-in swap. Same APIs, same generated Beam clients, same streaming responses. Flipping a source from direct to runner dispatch is one toggle in the Console. The query code on your app side doesn't change.

Install

Docker (recommended)

docker run --rm \
  -e SEMILAYER_RUNNER_ID=<runner-id> \
  -e SEMILAYER_RUNNER_TOKEN=rk_... \
  ghcr.io/semilayer/runner:latest

Pinning a version is strongly recommended in production:

docker pull ghcr.io/semilayer/runner:0.1
# or a specific patch:
docker pull ghcr.io/semilayer/runner:0.1.4

Published tags:

  • ghcr.io/semilayer/runner:latest — floating, latest published CLI
  • ghcr.io/semilayer/runner:<major>.<minor> — floating within a minor (picks up patches)
  • ghcr.io/semilayer/runner:<major>.<minor>.<patch> — immutable

npm CLI

Same bits, if you'd rather not use Docker:

npm install -g @semilayer/runner-cli

semilayer-runner start \
  --id <runner-id> \
  --token rk_...

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: semilayer-runner
spec:
  replicas: 2
  selector: { matchLabels: { app: semilayer-runner } }
  template:
    metadata: { labels: { app: semilayer-runner } }
    spec:
      containers:
        - name: runner
          image: ghcr.io/semilayer/runner:0.1
          env:
            - { name: SEMILAYER_RUNNER_ID, valueFrom: { secretKeyRef: { name: semilayer, key: runner-id } } }
            - { name: SEMILAYER_RUNNER_TOKEN, valueFrom: { secretKeyRef: { name: semilayer, key: runner-token } } }
          readinessProbe:
            httpGet: { path: /health, port: 8080 }
            initialDelaySeconds: 5

Runners maintain their own presence on the gateway via heartbeat — the /health shim is for the orchestrator.

Quickstart (5 minutes)

  1. Mint a runner. Open Console → RunnersAdd runner → name it → copy the rk_ token (shown once).
  2. Create a source. Console → your env → SourcesConnect source. Pick your bridge. Under Credentials location:
    • Managed — SemiLayer stores an encrypted connection URL. The runner reads the decrypted creds over the socket at job time.
    • Runner-local — no config on the SemiLayer side. You pass the DB URL via SEMILAYER_SOURCE_<NAME>_URL env var on the runner container.
  3. Assign the runner to the source. Back on the runner's row, toggle the source under Assigned sources.
  4. Start the runner. docker run (above). Within 5 seconds the runner appears as Online in the Console.
  5. Query. Hit the normal /v1/query/<lens> endpoint. Rows come back over api.semilayer.com — but the actual DB connection happened inside your network.

Full walkthrough: semilayer.dev/runners/install.

Configuration

All configuration is environment variables. Flags work too if you prefer them (see semilayer-runner start --help).

Env var Required Description
SEMILAYER_RUNNER_ID yes From the Console on create.
SEMILAYER_RUNNER_TOKEN yes rk_..., shown once on create.
SEMILAYER_GATEWAY_URL no Defaults to wss://runner.semilayer.com/connect. Override for air-gapped or self-hosted.
SEMILAYER_SOURCE_<NAME>_URL runner-local only Per-source DB URL when credentialsLocation='runner-local'. Source name is upper-snake-cased — source main-dbSEMILAYER_SOURCE_MAIN_DB_URL.
SEMILAYER_LOG_LEVEL no debug / info / warn / error. Default info.
SEMILAYER_HEALTH_PORT no Listener port for /health. Default 8080. Set to 0 to disable.

Modes of operation

Managed-credentials mode — SemiLayer encrypts and stores your DB URL. The runner receives it over the authenticated socket at job time, opens a connection, executes the query, disconnects. SemiLayer holds ciphertext + the ability to decrypt; your DB still never takes an inbound connection from SemiLayer's IPs because the runner is what connects.

Runner-local credentials mode — SemiLayer knows a source exists by name + bridge, nothing more. The config column in the SemiLayer DB is literally {}. The runner reads the DB URL from its own environment. If you rotate your DB password, you update the env on the runner and restart — SemiLayer has nothing to rotate, nothing to forget, nothing to leak.

Full details: semilayer.dev/runners/airgap.

Security

  • rk_ tokens are shown once on create. SemiLayer stores only the SHA-256 hash — the plaintext is not recoverable from our side.
  • Tokens are scoped to a single runner row and can be revoked instantly from the Console; the next heartbeat (~25s) closes any open socket.
  • All runner traffic rides TLS 1.3. The runner validates the gateway certificate before sending the token.
  • The image runs as non-root (USER node) and has no shell tooling beyond what node:22-slim ships.
  • No SSRF surface, no inbound listeners beyond the optional /health port.

Responsible disclosure: SECURITY.md.

Docs

Topic Link
Overview semilayer.dev/runners
Install semilayer.dev/runners/install
Source assignments semilayer.dev/runners/sources
Multiple runners / HA semilayer.dev/runners/multiple-runners
Airgap mode semilayer.dev/runners/airgap
Troubleshooting semilayer.dev/runners/troubleshooting
Security posture semilayer.dev/runners/security

Where the code lives

This repo is documentation + container artifact metadata for ghcr.io/semilayer/runner. The runner implementation ships as the @semilayer/runner-cli npm package; the Docker image is a node:22-slim base with that package installed globally.

Source for the runner implementation lives in the main SemiLayer repo. Issues for the runner specifically can be filed here; issues that span the platform belong in the main SemiLayer issue tracker.

License

MIT — see LICENSE.

About

Public community for the runner docker image

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors