CLANKERNET

Deploy your own clanking machine.

The machine is a factory: it builds engineering environments for whatever you are shipping. Commit a factory.yml to any repository, run one workflow, and every engineer on it gets a Traefik-fronted developer container on a host in your own cloud accounts — its own hostname, its own Postgres, SSH over your tailnet, the coding-agent tooling already inside — with the GitFWD coordination gateway standing beside them.

Open the app See how it works

Built on

GitHub Actions Pulumi Hetzner Cloudflare Tunnel Tailscale Docker + Traefik

How it works

Three steps. No hosted control plane.

A factory is one host, the developer containers it runs and the coordination gateway in front of them, described by two files with two owners: factory.yml says what to run and lives in your repo — any repo, building any product; a host spec says where and belongs to whoever owns the accounts.

  1. 01

    Commit factory.yml

    Image, ports, services, the env names a container may see, a hostname template, and a pointer to the host spec. Twenty lines is a complete one — see below.

    git add factory.yml && git push
  2. 02

    Run the workflow

    A reusable GitHub Actions workflow executes in your repository, with your credentials, from a SHA you pinned. It provisions the host once, the coordination gateway beside it, and a container per engineer. Nothing of yours is sent anywhere we can read it.

    gh workflow run factory-deploy.yml
  3. 03

    Log in

    Your container answers on its own hostname under your zone, behind Cloudflare Tunnel and Traefik, with sshd on your tailnet. Postgres is up, the repo is cloned, tmux is waiting.

    ssh [email protected]

factory.yml in twenty lines.

Everything a developer container needs is declared here and generated from here: the compose environment: block, the Traefik routes, the env file, the tmux windows. The image stays generic; the repo-specific part is this file.

  • Owned by the adopting repo — the host spec it points at is owned by whoever holds the accounts
  • Secrets by name, never by value; a container receives exactly the names in env.secrets
  • One hostname per (user, repo) by default; per-port hostnames for anything you expose
  • Validated against a published JSON schema before a single resource is touched

Read the manifest reference →

factory.yml
version: 1
repo:
  name: hello
  path: /home/developer/hello
host:
  ref: ./hosts/acme.yml   # WHERE: accounts, state
image:
  base: ghcr.io/clankerlabs/factory-base:1
hostnames:
  default: "{user}-{repo}.{zone}"
  port: "{port}-{user}-{repo}.{zone}"
ports:
  - { name: web, port: 3000, public: true, default: true, health: /healthz }
services:
  postgres: { databases: [hello], exports: { DATABASE_URL: hello } }
  procfile: Procfile
env:
  secrets: [OPENAI_API_KEY]   # names, never values
  git: { tokenFrom: secret:GIT_TOKEN }
hooks: { provision: [npm ci] }

Bring your own cloud

What you bring. What we hold.

Every credential stays with the host owner. The control plane is a workflow definition that runs under your permissions; revoking us is deleting one file.

Seam What you bring What we hold
Compute A Hetzner project and API token — or any Ubuntu 24.04 VM you already run, with root SSH Nothing
Ingress + DNS A Cloudflare account, one zone, and a token scoped to Tunnel edit + Zone:DNS edit. Only records tagged managed-by=factory/<host> are ever touched Nothing
Access A Tailscale tailnet and OAuth client, or plain SSH on the host's address. Developer keys come from github.com/<login>.keys by default Nothing
Secrets A Pulumi ESC environment, stack config, or a secrets file — referenced by name, never committed as a secure: blob Nothing
State Pulumi Cloud under your org, or an S3-compatible bucket (R2 works) with your passphrase or KMS key Nothing
Git A PAT with repo scope, held in your secrets provider, for the container's clone and push Nothing
Code A caller workflow pinned to our SHA, and the diff you read before bumping it The engine, the reusable workflow and the base image — reviewable in git, signed with provenance

No inbound path, no telemetry, no secret in a stack output — each of those is a test in the repo, not a sentence on a page. The bill of materials, what is in your state, the shared-responsibility split and the honest v1 limits are in the BYOC page.

Ways to run it

Operated by us today. Self-serve when the product is public.

Either way, what lands on your infrastructure is the factory — environments for your product, never a product of ours. The engine is BYOC by construction; what differs is who runs the workflow.

Deployed mode
Operated
Available now
A factory on your own infrastructure — a dedicated host, the coordination gateway, one container per engineer — provisioned and kept converged by us
For the repository you name: your hostnames on your zone, access on your tailnet
Access you grant and can withdraw; every action in your own audit trail
Scoped per engagement
How deployed mode works →
Self-serve
factory.yml
When the repository is public
Your repo calls the reusable workflow directly, pinned to a SHA
Pull the base image from a public registry — no pull token
Same engine, same host spec, same tests
Not offered yet — the repository is private while we run it for ourselves first
What changes when it opens →

Building with Clanker? The hosted product is at clanker.engineer

Get started

Your machines. Your accounts. Our factory.

The app is where your team coordinates around the machines — sign in with GitHub. To run a factory of your own, on your own infrastructure, talk to us about deployed mode.

Open the app

Want a factory in your cloud?

Deployed mode