npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@telorun/k8s-runner

v0.9.1

Published

Telo Kubernetes Runner - HTTP service that runs Telo Applications as sandboxed Kubernetes Pods. A backend over @telorun/runner-core, sibling to docker-runner.

Readme

@telorun/k8s-runner

HTTP service that runs Telo Applications as sandboxed Kubernetes Pods. A backend over @telorun/runner-core, sibling to docker-runner — it presents the identical /v1 session contract (/v1/health, /v1/capabilities, /v1/probe, /v1/sessions, /v1/sessions/:id/events SSE, /v1/sessions/:id/io WS) but spawns a Pod per session instead of a container. On /v1/capabilities it advertises image / pullPolicy as readOnly (server-enforced — the runner serves untrusted code under a hard-ceiling policy), so the editor lets the user edit only the runner URL.

⚠️ Security posture

The runner is a dumb executor: no auth, no per-IP concurrency, no rate limiting — only a global RUNNER_MAX_SESSIONS backstop. Do not expose it directly to anonymous internet traffic. Front it with the Telo Cloud control plane (or an equivalent abuse-control proxy), which resolves identity → tier → limits and forwards to the runner. Standalone, it is safe only on a trusted/internal network.

Resource limits are hard ceilings: a request may ask for less than the configured cap but never more (min(requested, ceiling)). For a bare runner serving an anonymous tier, the ceiling is the policy.

How it works

Per session the runner resolves the image, creates a Pod (telo run), watches it for status, attaches a PTY over the Pod attach subresource for the interactive /io channel, and — when an ingress base domain is configured — creates a per-session Service + Ingress (<sessionId>.<base-domain>) garbage-collected via an ownerReference to the Pod.

Every session runs a prebuilt per-app image — there is no in-pod install path. On a session-create the runner stages the bundle plus a generated Dockerfile (FROM <base>, ENV TELO_CACHE_DIR=/telo-cache, RUN telo install), runs a trusted Kaniko Job in the telo-builds namespace that bakes every controller and module manifest into /telo-cache/{manifests,npm} and pushes the result, then runs the session pod from that image — so the session never installs anything on the start path and a slow/unreachable package registry can't stall it.

The image is keyed on the dependency closure, not the whole bundle — the imports: set plus any controllers declared by inline Telo.Definition docs (see extractDependencyKey). A body-only edit (resource config, CEL) reuses the existing image; only an import or controller change rebuilds. The per-session body is delivered at boot, not baked: a body-fetch initContainer untars the staged bundle into a writable /app emptyDir, and the session runs telo run /app/<entry> --no-cache-write — reading the baked deps from the read-only /telo-cache and validating in-memory, so readOnlyRootFilesystem stays on with nothing written to the cache. Builds are existence-checked before building and single-flighted; a build failure surfaces as an actionable error carrying the build pod's log tail. RUNNER_IMAGE_REPOSITORY is therefore required — the runner refuses to start without a registry to build into.

Coming-up progress is reported over the /v1 SSE stream as progress events (buildprovision) while the session is still starting. The session is created with a fast 201 carrying the streamUrl before the build runs, so the client connects immediately and sees build + provision progress live; the backend then runs in the background and a start failure surfaces as a terminal failed status on the stream. The session flips to running when the Pod reaches Running.

Sandbox hardening is always on (non-root, read-only rootfs, drop-all caps, no service-account token, seccomp RuntimeDefault); a sandbox RuntimeClass (gVisor/Kata) is layered on when configured. In the prebuilt path the per-app image is single-tenant, so install scripts run normally inside the trusted build (native/postinstall controllers work) with no cross-tenant cache to poison.

Configuration (env)

| Env | Default | Purpose | | --- | --- | --- | | RUNNER_SELF_URL | (required) | Runner's in-cluster base URL (bundle fetch) | | PORT | 8062 | HTTP listen port | | RUNNER_DISPLAY_NAME | Telo Runner | Display name advertised on /v1/capabilities (the editor's runner label) | | RUNNER_DESCRIPTION | Runs the Telo application in a cloud environment | Description advertised on /v1/capabilities | | RUNNER_APPS | (unset → no apps) | JSON map of operator-predefined apps launchable by name (chart: inline apps.catalog, or apps.catalogSecret referencing a Secret holding the JSON — use the Secret whenever entries embed secrets in env); see the docker-runner README for the entry shape. App sessions run the catalog image directly as a pod — no on-cluster build | | RUNNER_APP_MAX_CPU | 500m | CPU ceiling for predefined-app pods (separate from the anonymous-session ceiling) | | RUNNER_APP_MAX_MEMORY | 512Mi | Memory ceiling for predefined-app pods | | RUNNER_APP_MAX_TTL_SECONDS | 21600 | Wall-clock TTL for predefined-app pods (agent sessions are long-lived) | | RUNNER_APP_MAX_EPHEMERAL_STORAGE | 1Gi | Ephemeral-storage ceiling for predefined-app pods | | RUNNER_SESSION_NAMESPACE | telo-sessions | Namespace for session objects | | RUNNER_IMAGE | telorun/node:latest-slim | Default base image; always offered in the picker and the fallback when the catalog is unreachable | | RUNNER_INIT_IMAGE | busybox:stable | Build-context fetch initContainer image (wget + tar) | | RUNNER_RUNTIME_CLASS | (unset → runc) | Sandbox RuntimeClass (gvisor/kata) | | SESSION_INGRESS_BASE_DOMAIN | (unset → logs-only) | Wildcard base for per-session ingress | | SESSION_INGRESS_TLS_SECRET | (unset → no TLS block) | kubernetes.io/tls Secret (in telo-sessions) the session Ingress presents; must cover *.<base-domain>. Set for Cloudflare Full (Strict) / any origin-cert upstream | | RUNNER_MAX_CPU | 50m | CPU ceiling | | RUNNER_MAX_MEMORY | 100Mi | Memory ceiling | | RUNNER_MAX_TTL_SECONDS | 3600 | Wall-clock TTL (Pod activeDeadlineSeconds) | | RUNNER_MAX_EPHEMERAL_STORAGE | 512Mi | Per-Pod ephemeral-storage ceiling | | RUNNER_MAX_SESSIONS | 32 | Global session backstop; at capacity the oldest exited session is evicted before a new run is rejected | | RUNNER_EXIT_TTL_MS | 14400000 | How long exited sessions stay in the registry (so the editor can re-attach and replay their history after a reload) before eviction | | RUNNER_TERMS_FILE | (unset) | Path to the agreement file (plain text / markdown), read at startup — mount it from a ConfigMap (e.g. /etc/telo/terms.md). Setting this (or RUNNER_TERMS_BODY) enables terms: the runner advertises them on /v1/capabilities and rejects POST /v1/sessions with 428 unless the client sends x-telo-accepted-terms matching the version. An unreadable path fails startup. The public cloud should set this | | RUNNER_TERMS_BODY | (unset) | Inline agreement text, for short notes; ignored when RUNNER_TERMS_FILE is set | | RUNNER_TERMS_TITLE | Usage agreement | Heading shown above the agreement | | RUNNER_TERMS_VERSION | (hash of body) | Acceptance version; defaults to a content hash so any edit to the body automatically re-prompts every client. Set explicitly only to control material-change vs typo |

Base-image picker

The runner advertises a menu of base images on /v1/capabilities, resolved from a Docker Hub repo's tags (filtered) and cached. The editor renders it as an editable image dropdown; the chosen image is re-validated server-side against the same list, so a client that skips the editor can't widen the set. RUNNER_IMAGE is always offered and is the fallback when Docker Hub is unreachable. Disable the catalog to lock image to RUNNER_IMAGE.

Pinned tags (e.g. 0.30.1-slim) are immutable. A picked moving tag like latest-slim only refreshes when the session's pullPolicy is always: the build then pins the per-app image to the base's current digest, so a moved tag yields a new image and rebuilds (otherwise the cached build — keyed on the tag string — is reused). Movement detection reads the digest from Docker Hub, so a base hosted elsewhere (GHCR, a private registry) can't be tracked — always degrades to reusing the cached build for it, same as missing / never.

| Env | Default | Purpose | | --- | --- | --- | | RUNNER_BASE_IMAGE_CATALOG_ENABLED | true | Resolve + advertise the picker; false locks image to RUNNER_IMAGE | | RUNNER_BASE_IMAGE_REPO | telorun/node | namespace/repository queried on Docker Hub | | RUNNER_BASE_IMAGE_PINNED_ONLY | true | Keep only pinned MAJOR.MINOR.PATCH[-variant] tags (drops latest, 0, 0.30) | | RUNNER_BASE_IMAGE_EXCLUDE_SHA | true | Drop commit-hash tags | | RUNNER_BASE_IMAGE_EXCLUDE_PRERELEASE | true | Drop semver prereleases (-rc.1, -alpha); -slim / -rust-* variants are kept | | RUNNER_BASE_IMAGE_INCLUDE | (unset) | Regex a tag must match (escape hatch) | | RUNNER_BASE_IMAGE_EXCLUDE | (unset) | Regex that drops a matching tag (escape hatch) | | RUNNER_BASE_IMAGE_LIMIT | 20 | Cap on advertised tags (newest first) | | RUNNER_BASE_IMAGE_REFRESH_SECONDS | 3600 | Catalog re-fetch cadence |

Image build (required)

| Env | Default | Purpose | | --- | --- | --- | | RUNNER_IMAGE_REPOSITORY | (required) | Registry repo for per-app images; tag = bundle hash | | RUNNER_BUILD_NAMESPACE | telo-builds | Namespace the trusted Kaniko build Jobs run in | | RUNNER_BUILDER_IMAGE | gcr.io/kaniko-project/executor:latest | Image builder | | RUNNER_BUILD_TIMEOUT_SECONDS | 600 | Build Job deadline / wait budget | | RUNNER_REGISTRY_INSECURE | false | Push/pull over HTTP / self-signed | | RUNNER_REGISTRY_API_URL | (unset → always build) | HTTP(S) base for the manifest existence check (authenticated via the push Secret) | | RUNNER_REGISTRY_PUSH_SECRET | (unset) | dockerconfig Secret (in telo-builds) Kaniko pushes with; also authenticates the existence check | | RUNNER_IMAGE_PULL_SECRET | (unset) | dockerconfig Secret (in telo-sessions) the kubelet pulls per-app images with | | TELO_REGISTRY_URL | https://registry.telo.run | Telo module registry used by telo install |

Deploy (Helm)

The runner requires a registry to build into (there is no in-pod install fallback), so point it at one your cluster's kubelet can pull from:

helm install telo-runner ./chart \
  --set build.repository=registry.example.com/telo-sessions \
  --set-file registry.dockerconfigjson=./dockerconfig.json \  # private-registry auth
  --set session.runtimeClass=gvisor

For a private registry, registry.dockerconfigjson creates the dockerconfig Secret in both namespaces (push in telo-builds, pull in telo-sessions) and wires RUNNER_REGISTRY_PUSH_SECRET + RUNNER_IMAGE_PULL_SECRET — the kubelet needs the pull copy because the per-app image is private. (Or manage the Secrets yourself and reference them via build.pushSecretName / build.pullSecretName.) The push Secret doubles as the credential for the manifest existence check, so the runner can see an already-built image in a private registry and skip the rebuild — without it a private registry answers 401 and every run rebuilds. A no-auth registry needs none of this.

The chart provisions the static scaffolding: the runner Deployment (single replica — the registry is in-memory and the runner reaps orphaned pods on boot), Service, scoped RBAC, the telo-runner / restricted-PSS telo-sessions / baseline-PSS telo-builds namespaces, a ResourceQuota, and NetworkPolicies (session pod-to-pod isolation + the build namespace's registry egress). The runner creates per-session and per-build objects at runtime.

The optional in-cluster registry (--set registry.enabled=true --set build.insecureRegistry=true) derives build.repository for you, but works only on clusters whose nodes are configured to trust it — otherwise an external/ cloud registry is simpler. Installing with neither a build.repository nor registry.enabled is rejected at template time.

Origin TLS (Cloudflare et al.)

To have the per-session Ingress present an origin cert (so an upstream like Cloudflare in Full (Strict) mode validates the origin), give the chart a kubernetes.io/tls Secret in telo-sessions. The cert must cover the wildcard *.<sessionIngress.baseDomain> — session hosts are a single label (<port>-<sessionId>.<base-domain>). Two ways:

# A — reference a Secret you manage in telo-sessions (cert-manager, your own sync)
helm install telo-runner ./chart --set sessionIngress.tls.secretName=telo-origin-tls

# B — let the chart create the Secret from your cert + key
helm install telo-runner ./chart \
  --set-file sessionIngress.tls.cert=origin.pem \
  --set-file sessionIngress.tls.key=origin.key

Either wires SESSION_INGRESS_TLS_SECRET, and the runner stamps a spec.tls block on every session Ingress. Leave all three empty to skip TLS at the origin (terminated entirely upstream).

Egress notes. (1) The kubelet pulls session images directly and does not use cluster DNS, so the in-cluster registry only works where nodes trust it (e.g. containerd registries.conf). (2) Core NetworkPolicy is CIDR-only and cannot express the package-registry FQDN allowlist the build namespace needs — use a CNI with FQDN policy (Cilium) or an egress proxy to tighten it.

Development

pnpm --filter @telorun/k8s-runner build   # tsc
pnpm --filter @telorun/k8s-runner test    # vitest (limits clamp, tar, bundle token)

The Kubernetes backend can't be exercised without a cluster; unit tests cover the backend-independent logic (limit clamping, the tar writer, bundle tokens).