Security & isolation
Where and how your code runs — made verifiable.
Your CI runs on our infrastructure, and every job runs in its own hardware-virtualized micro-VM. This page describes exactly how that isolation works, how long a job’s environment lives, and how you can independently verify the image your code runs on — without a sales call or an NDA.
Last updated: 2026-07-24 · v1.1
The isolation model
No single layer is the boundary. Untrusted CI code — your build steps, and anything they spawn, including Docker-in-Docker — is contained by several independent layers. If one is bypassed, the next still holds. This is defense-in-depth, not “trust the container.”
| Layer | Mechanism | What it contains |
|---|---|---|
| Hardware-virtualized micro-VM | Each runner pod runs inside its own micro-VM with its own guest kernel, using Kata Containers. A micro-VM is a lightweight virtual machine — not a shared-kernel container. | The primary boundary. CI code — and its Docker-in-Docker — cannot reach the host kernel or any other job’s VM. |
| Network default-deny | The runner’s network is deny-by-default, enforced in the kernel with eBPF / Cilium. It cannot accept inbound connections or reach into the cluster: private IP ranges, the cloud-metadata endpoint, the control plane, and neighbouring pods are all blocked, except an internal pull-through cache for packages and images. Outbound reaches the public internet. | Lateral movement, cloud-metadata credential theft, and reaching internal services. |
| No cluster credentials | The runner holds no token to the orchestrator (Kubernetes API). | The foothold for privilege escalation — even if a container is escaped, there is no cluster credential to pivot with. |
| Workload hardening | Inside the VM, the job container runs with all Linux capabilities dropped, privilege escalation disabled (no-new-privileges), and a seccomp profile. The one privileged component is the Docker-in-Docker engine — required to build images — and its privilege exists only inside the micro-VM, never on the host (see Layer 1). | Narrows what the job can do even before the VM boundary. |
| Node separation | Runners run on dedicated nodes, physically separate from the control plane, secrets, and platform data. | Blast radius: a compromised job is nowhere near the platform’s control plane or other tenants’ data. |
| Runtime threat detection | Hosts run runtime detection with eBPF / Tetragon for container-escape and privilege-escalation attempts. | Turns an attempted breakout into an observable, recorded event rather than a silent one. |
We name the primitives and say what each is for; we do not publish the policies, allowlists, versions, or topology behind them. Components are kept current on a security floor — specific version numbers are deliberately not published, since a version maps to a known set of CVEs.
Ephemeral by design
A job’s environment does not outlive the job.
- One job, one fresh VM. Every job gets a newly created micro-VM. Ephemeral means it exists only for that job.
- Destroyed on completion. When the job finishes, the VM and everything in it — checked-out code, environment, any secrets that reached it — are destroyed.
- No reuse across customers. A VM is never handed from one job to the next. There is no warm environment from which a later job could inherit residual code, caches, or secrets.
The boundary of a job is the boundary of trust.
Supply chain you can verify
The image your code runs on is built in public CI and is independently verifiable — you don’t have to take our word for what’s inside it.
Each badge links to its live record — the signature, provenance and SBOM are verifiable with the commands below.
Immutable images. Runner images are pinned by content digest (@sha256:), never a floating tag — the exact image you verified is the one that runs.
Every image built from main is signed with cosign keyless (Sigstore — Fulcio + Rekor, via GitHub Actions OIDC) and carries SLSA build provenance and a CycloneDX SBOM attestation. The signature is applied only after a green trivy HIGH/CRITICAL CVE scan of the published-by-digest artifact — so a valid signature also means the image passed the CVE gate.
Image: ghcr.io/tempusbuild/runner-ubuntu-24.04 — immutable digest tags, no floating :latest. Work against the @sha256: digest, not a tag:
IMAGE=ghcr.io/tempusbuild/runner-ubuntu-24.04
DIGEST=$(docker buildx imagetools inspect "${IMAGE}:vYYYYMMDD" --format '{{.Manifest.Digest}}')
1. Signature — binds the image to our build workflow and to GitHub’s OIDC issuer. Any other identity is not our build:
cosign verify \
--certificate-identity-regexp '^https://github\.com/tempusbuild/runner-images/\.github/workflows/build\.yml@refs/heads/main$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
"${IMAGE}@${DIGEST}"
2. Build provenance — a Sigstore-signed SLSA attestation, verified with the gh CLI:
gh attestation verify "oci://${IMAGE}@${DIGEST}" --owner tempusbuild
3. SBOM (CycloneDX) — a cosign attestation on the same digest. The SBOM is too large for the transparency log, so it is TSA-timestamped (RFC3161) instead of logged to Rekor; verification therefore skips the Rekor lookup but still checks identity, signature, and the trusted timestamp:
cosign verify-attestation \
--type cyclonedx \
--insecure-ignore-tlog \
--use-signed-timestamps \
--certificate-identity-regexp '^https://github\.com/tempusbuild/runner-images/\.github/workflows/build\.yml@refs/heads/main$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
"${IMAGE}@${DIGEST}"
--insecure-ignore-tlog waives only the Rekor lookup; the signature, the signing identity, and the RFC3161 timestamp are still verified. The signature in step 1 is the cryptographic anchor; provenance and SBOM bind to the same digest.
The signature and provenance are recorded in the public Rekor transparency log (открывается в новой вкладке). The full walkthrough and our CVE policy (открывается в новой вкладке) are in the runner-images SECURITY.md; the build workflow (открывается в новой вкладке) and the whole repository are open at github.com/tempusbuild/runner-images (открывается в новой вкладке).
Data & tenancy
- Location. Compute and backups run in the EU (Hetzner).
- Encrypted secrets. Kubernetes secrets are encrypted at rest in the cluster datastore, and our operational credentials are encrypted with age (sops) — never stored in plaintext, in Git or anywhere else.
- In transit. The dashboard and API are served over TLS (Let’s Encrypt), with HSTS enforced. Internal metrics and health endpoints are not exposed to the public internet.
- Your CI secrets are not ours to leak. We never see or store them: GitHub delivers them directly into your job’s micro-VM, which is destroyed when the job ends.
- Just-in-time job credentials. A runner receives only a short-lived, per-job registration token. No long-lived platform credential sits on the runner.
- Tenant data. Platform data — job history, balances, ledger — is isolated per tenant, so one customer’s data is not reachable from another customer’s context.
- Retention & deletion. Job metadata and security logs are kept only as long as needed for metering, security, and dispute resolution, then deleted or anonymized; billing records are retained for the statutory period. You can request deletion of your account and personal data.
- Recoverable backups. The database is continuously backed up with point-in-time recovery and a 30-day retention window.
Compliance & assessments
We state what exists today and what is on the roadmap — and nothing we haven’t earned.
Available now
- Verifiable supply chain — signed images, SLSA provenance, and SBOMs you can check yourself (above).
- Open runner images — the execution environment is public and readable.
- Published vulnerability-disclosure policy (below).
- Public status page — uptime and incident history at status.tempus.build.
On the roadmap
- Independent isolation assessment — a third-party penetration test of the isolation model.
- SOC 2 — we are working toward it.
We are not currently certified against SOC 2 or any equivalent. The claims on this page are the ones you can verify directly today. Assessment reports, once available, are shared under NDA:
Request a security report — there is no lead-capture form on this page.
Responsible disclosure
Found a vulnerability in the platform, the runner images, or the supply-chain pipeline? Report it responsibly:
- Email: security@tempus.build
- security.txt: tempus.build/.well-known/security.txt (RFC 9116)
- Runner images: privately via GitHub Security Advisories on tempusbuild/runner-images (открывается в новой вкладке)
Please don’t open a public issue for an unpatched vulnerability. We acknowledge receipt and keep you posted through triage and fix.
Subprocessors
We use a small number of third-party processors to run the service. Personal data is processed only as needed to deliver it.
| Subprocessor | Purpose | Location |
|---|---|---|
| Hetzner | Compute and backups | EU |
| GitHub | Source, identity (OAuth / GitHub App), CI secret delivery | US |
| Mailgun | Transactional email and notifications | EU |
| Dodo Payments | Payment processing (merchant of record) | US |
| OpenRouter | LLM inference for the optional Advisor feature — receives aggregated cost metrics and structural labels (e.g. repository and workflow names); no source code, no secrets, and the acting user is pseudonymized (hashed) | US |
This list is versioned; material changes are reflected here with the revision date above.