Ansible, Terraform, OpenTofu, Bash, PowerShell, Python, Go

One gate for every change to production.
And proof it held.

Run everything. Ansible, Terraform, OpenTofu, Bash, PowerShell, Python, and Go all go through the same path: request, policy, approval, whether a person or an AI agent asked. Nothing runs until it clears. Then watch every task land on every host, live. Every run leaves behind a cryptographically signed receipt anyone can verify offline against our published key, with no server of ours involved. An agent holding only a SwitchTender token has no path around that gate at all; for people it is the keys you stop handing out. We say which is which. One binary. Starts in seconds.

Already on AWX? switchtender import awx brings your projects, inventories, templates, surveys, and schedules across in one command. Preview the plan, then apply.

Don't take the receipts on faith. Pull the live demo's signed history with curl and check it with LoomSeal: Apache-2.0, a published format with conformance vectors, and a second implementation in Python. We publish LoomSeal too, so what you are trusting is the format and the vectors, not our word. Try to refute it →

Source-available. BSL 1.1. Go SDK with drop-in plugins. One file to back up.

switchtender: fleet health
Fleet health ranking hosts by recent failures, with flaky detection and per-host outcome sparklines

Why SwitchTender? A switchtender is the person who throws the track switches in a rail yard, deciding which train goes where. Nothing moves without them. That's the job here too: one control point for every playbook on every host.

1Requestrecorded before anything acts
2Policysees who's asking, can refuse
3Approvalbinds to the exact change
4Executiongated wherever it runs
5Evidencea receipt verified offline
1binary, zero Kubernetes
30mscold boot on a laptop
~35MBmemory at idle on Linux
7tools, one engine
Above execution

Governance the rest
of the field skips

Multi-tool execution is common now. The difference sits above execution: a control plane that proves what happened and enforces what's allowed.

  • Tamper-evident, signed audit. Alter one entry and the chain breaks. Prove the trail is intact, offline.
  • Change control by policy. A prod terraform destroy holds for an admin's sign-off, automatically.
  • Secrets masked, sourced, sealed. They never land in a log and never live in SwitchTender if you don't want them to.

Evidence you can hand to an auditor.

Most controllers keep an activity log: rows in their own database, editable by whoever holds the database. SwitchTender writes every change into a hash chain, then gives you six ways to carry the proof out of the building. What changed, who asked, and who approved it become things you show, not things you say. Every claim on this page is defended adversary by adversary, limits included, in the threat model.

A receipt for every change

Every mutation through the API returns an Audit-Receipt header naming its place in the chain. Keep it. Months later, switchtender audit receipt proves the chain still holds that entry at that position, and a server that dropped it can't answer.

A dossier for every run

The Evidence button on any run, or switchtender audit run, emits one self-contained file: the spec, the risk grade, who approved it, what happened on each host, and the receipts and anchors behind all of it. An auditor's sample request is answered in one export.

The change register

switchtender audit report renders every change in a period, the sample a SOC 2 CC8.1 or ISO/IEC 27001 A.8.32 review asks for; the control mapping and a sample pack show exactly what an assessor gets. Point --evidence-dir at an archive and registers write themselves on a cadence, so the evidence exists before anyone asks. The register needs Team. The receipts, dossiers, and signed bundles beside it are free.

A witness on another machine

switchtender witness watches the chain's public feed from a second machine, keeps a signed checkpoint of what it saw, and raises a finding when history shrinks or a beat goes missing. The hosted witness answers with countersigned attestations that verify offline against a key you pinned.

Receipts in your SIEM

Stream every audit entry into your SIEM as NDJSON or RFC 5424 syslog, each event carrying its chain receipt. Delivery is at-least-once behind a durable cursor, so an outage delays events instead of dropping them, and any sampled event redeems against the live chain.

Verified without trusting the server

Export the chain as a signed LoomSeal bundle and verify it on a machine with no network, with a verifier that is not this binary. LoomSeal is Apache-2.0 and we publish it as well, so the thing standing behind the check is a specified format with conformance vectors and a second implementation, which anyone can reimplement without us. Or drop it into your browser and watch the same verifier check it, offline, with nothing uploaded. A checker anchors the signing key without taking the server's word for it: each install publishes its key at /.well-known/loomseal.json, and you can pin a fingerprint handed over out of band, so the key is checked against something other than the bundle that carries it. The binary proves itself the same way: switchtender version --verify hashes the running build against the BINARY_SHA256SUMS published with its GitHub release.

Don't take our word for it. Check the live demo's chain right now.

These run against demo.switchtender.com, a live instance that wipes and reseeds its history every night. That makes the third command a live-fire exercise: become its witness today, run it again tomorrow, and the witness will refuse the rebuilt history with a finding. That alarm is the product working. A nightly reseed is exactly what a rewritten chain looks like from outside, and nothing the server says can talk your witness out of what it saw.

# 1. the server re-verifies its whole chain, entry by entry
curl -s https://demo.switchtender.com/v1/audit/verify

# 2. pull the whole chain as a signed bundle, yours to keep and verify anywhere
curl -s https://demo.switchtender.com/v1/audit/bundle > demo-bundle.json

# 3. verify that bundle offline against the key the demo publishes,
#    with LoomSeal, a verifier that is not this binary (install: loomseal.com,
#    or skip the install and drop the file on switchtender.com/verify)
loomseal verify demo-bundle.json --fingerprint "$(curl -s \
  https://demo.switchtender.com/.well-known/loomseal.json | jq -r .key_id)"

# 4. become its witness: keep a signed checkpoint of the head you saw.
#    Run it again after tonight's reseed and watch it catch the rewrite.
switchtender witness --server https://demo.switchtender.com --once

Bring the whole thing
with you.

A migration is one command, not a rebuild. AWX, Semaphore, Rundeck, and Jenkins each have an importer, and Ansible Automation Platform, Tower, and Ascender export through the same awxkit command with the same top-level keys, so the AWX importer reads them too. Even a crontab imports, line by line, into governed schedules. Export, preview exactly what will land, then apply. Projects, inventories with their hosts and groups, dynamic inventory sources, job templates, surveys, schedules, and credential shells all come across in one pass. A malformed asset is skipped with a warning instead of failing the import, so even a partial export migrates everything it can.

# 1. preview: read the AWX export, print the plan, write nothing
switchtender import awx awx-export.json --db switchtender.db

# 2. apply: create every project, inventory, template, survey, and schedule
switchtender import awx awx-export.json --db switchtender.db --apply

# on Semaphore instead? swap one word:
switchtender import semaphore export.json --db switchtender.db --apply

Credentials arrive as named shells, since these exports omit secret material by design. Re-enter each secret once and every template that references it works. The switching guide maps every AWX concept to its SwitchTender equivalent, and the AWX alternative page makes the full case.

Every feature below is free.

All of it ships in the binary you already deployed, no host caps, no seat caps, no run caps, no time limit. Paid tiers only add what never shipped free, and nothing on this page ever moves behind a paywall. Where a card names a Team line, it is named on the card.

Provable audit

Every change links into a tamper-evident SHA-256 hash chain; a change that can't be recorded is refused rather than made quietly. Export the chain as a signed LoomSeal bundle and verify it offline with an open, independent verifier, without trusting the server.

What it doesn't claim: the chain proves the record wasn't altered after writing, not that a compromised controller recorded everything. The full boundary statement is in the docs.

Whether you get a second chance

Risk says how bad a change is. It never says whether you can take it back, and the two come apart constantly: restarting a fleet is high risk and undoes itself, while deleting a backup set reads quietly and is permanent. Every run is graded reversible, costly, or irreversible, and an approval rule can hold on that grade. An Ansible run is graded from its playbook's own text, not guessed from a file name.

What it doesn't claim: roles and includes aren't followed, so the scan can only ever raise a grade, never lower one. Work it can't see makes a run look less safe, never more.

Your estate as it was

Every fact gather is kept instead of overwritten, so you can ask what was running on the date of your last audit, and what has moved since: host by host, fact by fact, with the value at each end. Most tools answer only what is true now, because reconstructing the past needs an ordered history nobody can quietly edit.

What it doesn't claim: history starts the day you install, not before. Every answer reports how far back the records reach, so an empty one is never mistaken for an empty estate.

The change, not just the run

Nobody means a run. They mean "we rolled out the fix", which was four runs, a rollback and a fix-forward, across three days. Label them and read the whole arc as one thing, with its span, everyone who acted, and an outcome worked out from the runs rather than typed in by whoever closed it.

What it doesn't claim: the outcome is derived, so it can't disagree with the runs. It also can't record an intent nobody wrote down.

Live host-by-task matrix

Read a run like a dashboard instead of a scrollback. Every host and task paints itself live as the run happens, and each cell opens to its own output and exit code.

Multi-tool engine

Ansible, Terraform, OpenTofu, Bash, PowerShell, Python, and Go, each with a dry run. One engine, one UI, one audit trail, on the host or in a pinned container image. Need an eighth? Write it in Go and plug it in.

Balanced splits

Shard a big job across hosts, balanced by each host's measured duration. Retry only the shards that failed, not the whole run.

Fleet memory

It flags flaky hosts, trends how long each task takes, and keeps every host's history across runs. Most controllers forget the moment a run ends.

Visual workflow editor

Drag steps from all seven tools into one graph. Fan out, fan in, retry any step, run the whole pipeline. No YAML by hand.

Schedules you can trust before you save

Type a cron expression and see its next firings before you save it. A scheduled launch carries its template's approvals, notifications, and timeout, and every run records which schedule started it.

Drift detection

A dry run shows exactly what has drifted, across Ansible hosts and Terraform directories, before you touch a thing. One click builds the fix, held for approval. None of the controllers in our comparison document it, and the Terraform control planes that do detect drift do not govern your Ansible fleet beside it. Detection is free. The one-click reconcile needs Team.

Enforced approvals

Hold risky runs for sign-off, gated by policy on tool, command, or target. Operators request, admins release. No skipping the gate. Community holds one policy. The full engine, with deny rules, risk floors, actor scoping, and distinct-approver separation of duties, is Team.

Roles, teams, and orgs

Global roles, teams, organizations, and per-object read, use, and manage grants. Delegate view of one credential without a global viewer, or a whole org inherits a project at once. All of it ships in the core, not a paid tier.

Reach isolated networks

A worker dials out to the control node with a token and opens no inbound port, so it runs jobs inside an air-gapped segment, a DMZ, or a customer network the control node can't reach. One binary, one outbound connection. Running any worker process needs Team.

Optional advisory AI

Off by default. Switch it on to triage a failure, answer a fleet question, or draft a run from a sentence. It proposes, never executes. Bring your own model: local Ollama, Claude, or any OpenAI-compatible API.

A doctor for your control plane

One click verifies every registered reference: templates to inventories, projects, and credentials, schedules to templates, every cron expression. Broken links surface with a fix button before they break a run.

Run it again, your way

Rerun any finished run with its exact spec, credentials included, one click. Or launch a template with overrides: host limit, inventory, credential picks, extra vars, dry run. Every override is authorized like a fresh launch.

Built for audits and sharing

Every list exports to CSV or JSON, filtered exactly as you see it. Every run exports its events, log, and per-host results. Copy a link to any run, in any of the three themes.

Every run knows where it came from

Each run records what fired it, the template, schedule, rerun, or drift fix behind it, and who asked. Add labels like env=prod, then search with fielded terms: status:failed host:web01 label:env=prod, resolved across your whole history.

Notifications everywhere

A finished run reaches Slack, Teams, Discord, Mattermost, Rocket.Chat, ntfy, email, or a webhook; a failure pages PagerDuty, alerts Grafana OnCall, or texts through Twilio. Eleven channels, server-wide or per template, so each team pages its own people.

Secrets kept out of the log

Sealed at rest, sourced live from Vault, AWS, Google, Azure, CyberArk, or 1Password, and kept off the command line so nothing shows in the process table. A tool that prints a secret shows ***. Typed cloud credentials inject the exact variables each tool expects.

What it doesn't claim: log masking matches the secret's literal text, so a value a tool re-encodes as base64 or hex, or one shorter than four characters, is not caught. Every tool in the field has the same limit; treat it as a safety net, not a boundary.

See it in action

A run painted live as a host-by-task matrix, not a wall of scrollback. The colors speak Ansible: green means ok, amber means the task changed the host, red means failed, gray means skipped.

switchtender: run

Design pipelines by dragging, not by YAML

Wire Ansible, Terraform, OpenTofu, Bash, PowerShell, Python, and Go steps into a graph, plus any tool you plug in. Fan out, fan in, and run the whole thing as one pipeline with per-step retries. It runs in the same binary as everything else.

switchtender: workflow editor
AI agent change governance

Agents are principals here, not API keys.

An agent's token is minted as an agent's, naming the human it acts for, and capped below admin so it can never approve its own work or touch identity, access, or secrets. Policy can say what an agent may do, what needs a person first, and what it may never do at all. Every change lands in the chain as the agent's, on behalf of its human, and the receipt proves a person approved exactly the change that ran. The gate is the agent's only door. We don't build the best AI agent. We govern what agents are allowed to do.

Optional layer, off by default

None of this needs AI.
Turn it on if you want it.

Everything above runs with no model anywhere near it: the live matrix, the balanced splits, the governance, the audit chain. Advisory AI is an add-on, and nothing else depends on it.

Switch it on and it proposes a diagnosis for a failed run, answers a plain-English question about your fleet, or drafts a run from a single sentence. That's all it does. It can't execute anything. Every draft waits at the same approval gate you do, and it never sits in the execution path. Bring your own model: Ollama on your hardware, Claude with your Anthropic key, or any OpenAI-compatible endpoint. Secrets are masked before a model sees a byte.

switchtender: AI triage
A failed run with the AI triage panel isolating db01's apply step as the root cause and suggesting next steps
switchtender: ask your fleet
The overview page answering a plain-language question about which hosts are failing and where to start

AI advises. SwitchTender enforces.

Extensibility: SDK + drop-in plugins

Seven tools in the box.
Add more in Go.

Ansible, Terraform, OpenTofu, Bash, PowerShell, Python, and Go all run out of the box, no plugins required. When you need an eighth tool, a custom secret backend, a new AI provider, or your own notification channel, you write it in Go against a stable SDK. Compile it into the binary, or build it once and drop it into --plugins-dir, where a stock release loads it at startup over gRPC with mutual TLS and treats it as its own. A plugged-in tool submits, validates, and audits like a built-in. A plugged-in channel gets every terminal run with secret-carrying vars already redacted. The SDK guide walks both paths and carries a complete extension you can build and drop in.

// build once, drop into --plugins-dir, no recompile of switchtender
import (
	"github.com/kordloom/switchtender/sdk"
	"github.com/kordloom/switchtender/sdk/plugin"
)

func main() {
	plugin.Serve(&plugin.Extension{
		Tools:     map[string]sdk.ToolRunner{"packer": sdk.ToolRunnerFunc(runPacker)},
		Notifiers: map[string]sdk.Notifier{"pagerduty": sdk.NotifierFunc(page)},
	})
}

The honest side-by-side

How SwitchTender measures up against the tools teams actually shortlist. Where it's still young, the comparison doc says so plainly. Go deeper on any one: AWX, Ansible Automation Platform, Ascender, and Semaphore. How runs execute under load covers the engine underneath.

CapabilitySwitchTenderAWXAAP / TowerSemaphoreRundeck
Deploy without KubernetesVM installer
Live host-by-task matrixper-host summaryper-host summaryper-node, per-step
Ansible, Terraform, OpenTofu, Bash, PowerShell, Python, GoAnsible onlyAnsible onlyno Go, no uniform dry runscripts, no native IaC
Duration-balanced job splittinground-robinround-robin
Fleet memory: flaky hosts, trends
Tamper-evident, signed auditactivity logactivity log
Secret values auto-masked in logsopt-inopt-insecure options
Enforced approval policiesbasicbasicenterprise
One-command migration in
Drift detection from a dry run
Drag-and-drop workflow editorlinear steps
Advisory AI, read-only: triage, answers, drafts, never executesauthoring assist
Drop-in Go plugins: tools, secrets, AI, notifiersJava plugins
Container execution for every toolAnsible onlyAnsible only
Typed cloud credentials, pluggable typestyped, not pluggabletyped, not pluggable
Organizations, teams, per-object grantsteams, four fixed rolespolicy files, no GUI editor
Instance groups: pin work to worker pools TeamProenterprise
Active-active HA on PostgreSQL creating the schema is Team; opening an existing one is never gatedvia Kubernetesvia Kubernetesenterpriseenterprise

Every row was checked against each vendor's own documentation on 10 August 2026, at AWX 24.6.1, Ansible Automation Platform 2.7, Semaphore 2.19.7, Ascender 25.4.0, and Rundeck 6.0.1. Versions were re-checked on 7 September 2026: AWX is unchanged, Semaphore is now 2.19.12, Ascender 25.6.1, and Rundeck 6.1.0. All five of these products keep shipping, so a table like this starts going stale the day it's written. If a row is wrong, open an issue and we'll fix it.

Running in seconds

One binary, one SQLite file. No control plane to stand up first.

# install (or grab a release binary)
go install github.com/kordloom/switchtender@latest

# serve: SQLite by default, PostgreSQL optional
switchtender serve --addr :8080 --db switchtender.db

# open the UI
open http://localhost:8080/ui/

Three looks. Pick your line.

The whole UI ships with three themes. Loom is the signature look, Linen is stark white, Ink is warm black. Switch in one click from the sidebar, or link any page with ?theme=.

Every theme keeps the same status colors, so a failed host reads the same in all three.

Every run leaves a receipt.

Who asked, who approved, what changed on every host, and proof that none of it was edited after the fact. Human or AI agent, the record is the same, and anyone can check it offline without trusting your server or ours.

Self-hosted, nothing phones home, and a lapsed license takes nothing with it. You never bet your operations on our survival.