Skip to content

Blog

From GitHub Actions to Appaloft: what evidence a deployment should leave behind

How Appaloft thinks about GitHub Actions deployments as auditable evidence: commit SHA, image digest, config snapshot, provenance, readback, and rollback.

AppaloftGitHub ActionsDeployment controlProvenanceAI

GitHub Actions is a good place to trigger deploys.

A merge, tag, release, or pull request label can start a workflow. The workflow can check out code, install dependencies, build an image, upload an artifact, and call a CLI or API. For a team, that is usually more reliable than relying on one person’s laptop.

But a successful workflow does not mean the deployment left enough evidence behind. A few weeks later, when someone asks “what is production running?”, a green Actions run is not a complete answer. You still need to know which commit was deployed, which artifact was built, which configuration was applied, which image digest the runtime actually pulled, what health check passed, and where rollback should point.

This post follows A deployment control plane for the AI era, Skills are not MCP, and When an AI agent builds a static site, deployment should still be a workflow. GitHub Actions can be the entrypoint. Appaloft is more interested in the evidence that survives after the workflow finishes.

CI Logs Are Not Deployment State

Many deployment reviews eventually come back to a plain question: what did we actually deploy?

A branch name cannot answer that. main keeps moving, a release branch can be updated, and a latest tag can be replaced. A commit SHA is better, but it only identifies source code. It does not identify the runtime artifact. A Docker image tag is also not enough, because a tag is a name, not immutable content.

The deployment fact is closer to a set of bindings:

  • source revision: repository, commit SHA, submodule SHA, or source snapshot;
  • build execution: workflow path, run id, runner environment, and build command;
  • artifact identity: image repository, tag, and immutable digest;
  • config identity: deploy config, environment references, secret refs, and config snapshot digest;
  • deploy request: actor, target environment, resource, request id, and idempotency key;
  • runtime readback: pulled digest, health check, URL, logs, and status;
  • recovery target: previous verified digest, previous config snapshot, or a named manual gap.

CI logs help during diagnosis, but they are not control-plane state. They can be truncated, expired, permissioned away, and hard for the CLI, Web console, AI agents, and later workflows to consume consistently. The deployment control plane needs the important facts in structured form.

Commit SHA Is Not Enough

We like Git SHAs because they give source code a hard name. The catch is that deployment is not just Git.

The same commit can produce different artifacts when the base image changes, a package registry changes, build parameters differ, a secret ref is missing, or a submodule checkout is not the same. Even if the build is reproducible, the runtime may pull a different image if it follows a tag. In the other direction, one already-built image digest can be deployed to multiple environments with different config snapshots.

That is why the evidence chain should keep both SHA and digest.

The SHA tells us where the source came from. The digest tells us which artifact is actually running. The config snapshot digest tells us the deploy was bound to the runtime configuration at that moment, not only to code. If any of those are missing, audit and rollback become fuzzy.

For Appaloft, entrypoints such as image://... and prebuilt-image are not a way for CI to bypass the control plane. They are a way to pass an artifact that Actions already built into Appaloft, so Appaloft can still plan, execute, verify, and recover the deployment.

A simplified command can look like this:

appaloft deploy image://ghcr.io/acme/web:pr-123-abc123 \
  --config appaloft.yml \
  --preview pull-request \
  --preview-id pr-123

The important thing to preserve is not that exact command. It is the source, artifact, config, target, and readback behind the command.

Provenance Should Be Boring

Provenance should not be an essay, and it should not be the entire CI log copied into storage. It is better as a small schema-versioned JSON document that both the control plane and humans can read.

A useful deployment provenance artifact can include:

  • schemaVersion;
  • source repository and commit SHA;
  • public dependency or submodule SHA, when the deployment depends on one;
  • workflow path, run id, and run attempt;
  • build source, such as Dockerfile path or build context digest;
  • image repository, tag, and digest;
  • deployment config path and config snapshot digest;
  • Appaloft CLI/API version or control-plane version;
  • target environment, resource id, or preview id;
  • actor, trigger event, and request id;
  • previous verified artifact for rollback;
  • external-impact flags such as createsExternalResources.

It should not include:

  • API tokens, deploy tokens, or GitHub tokens;
  • registry credentials;
  • database URLs;
  • SSH private keys;
  • webhook secrets;
  • OTLP, Sentry, Stripe, email, or object-storage secrets;
  • raw provider endpoints, account ids, or customer data that do not belong in an audit artifact.

That boundary matters. Provenance is evidence for audit, recovery, and diagnosis. It is not a place to move secrets.

The Deploy Request Should Reference Evidence

When GitHub Actions calls Appaloft, it does not need to cram every detail into a long command. A better shape is for the build step to produce a provenance artifact, and for the deploy request to reference it while carrying the key identities as structured payload.

For example:

source:
  kind: docker-image
  repository: ghcr.io/acme/web
  tag: pr-123-abc123
  digest: sha256:...
  provenanceRef: github-actions-artifact://run-123456/deploy-provenance.json
config:
  path: appaloft.yml
  snapshotDigest: sha256:...
target:
  environment: preview
  resource: web
  previewId: pr-123
rollback:
  previousVerifiedDigest: sha256:...

This is not a request for every project to become a complex platform on day one. It is the opposite: pull the minimum necessary facts out of terminal output. Which artifact, which config, which target, how to read it back, and how to undo it.

Appaloft’s role is to take that request and continue with a deployment plan, runtime execution, events, health checks, status, logs, readback, and explicit retry, redeploy, or rollback paths when something fails.

Readback Should Prove What The Runtime Pulled

It is not enough for the build system to say “I pushed the image.”

The deployment control plane should read back several facts:

  • the registry contains the image digest;
  • the runtime pulled the digest, not only a moving tag;
  • workload, service, or container metadata can be associated with the request id;
  • the health endpoint or access check passed;
  • logs/status reference the same deployment attempt;
  • the environment’s active revision points at the verified artifact;
  • the rollback candidate points at a previous verified artifact instead of a guess.

Some readback requires a provider adapter. Some can be recorded by the Appaloft runtime. Some may only be marked as deferred in local development or preview environments. That difference should be visible instead of being hidden behind a green CI status.

This is also where Appaloft becomes useful to AI agents. An agent should not SSH into a host and guess the container version. It should not scrape long GitHub Actions logs. It should ask the control plane: what is the current deployment attempt, what digest is active, where is the health evidence, and what rollback target is available?

Rollback Should Target Something Verified

“Roll back to the previous version” sounds simple. In practice, teams often have to ask what previous means.

Is it the previous commit? The previous image tag? The previous workflow run? Or the previous artifact that actually passed health checks? If the last deploy also changed environment variables or dependency resources, is code rollback enough?

Appaloft’s preferred shape is rollback to a verified target:

  • the previous successfully activated image digest;
  • the matching config snapshot digest;
  • the resource/profile readback from that moment;
  • the known health result;
  • a clear manual gap when automatic recovery cannot be promised.

That makes rollback a new control-plane operation, not “run an old script again.” Choose a verified target, produce a plan, confirm impact, apply the switch, and read back the result.

The same model helps preview deployment. Pull request preview cleanup should not delete resources by vague filters. It should find resources by preview id, source fingerprint, provenance label, or deployment record. Deployment and cleanup both need evidence; otherwise more automation just creates more unexplained leftovers.

GitHub Actions Is Still Only The Entrypoint

GitHub Actions is valuable because it puts deployment into repository events, review, secret management, and execution history. Appaloft is not trying to replace that.

But Actions should not become a second deployment product. A workflow should not hand-write a pile of provider SDK calls while Appaloft watches from the side. The cleaner split is:

  • GitHub Actions handles checkout, test, build, artifact publishing, and provenance;
  • Appaloft handles deployment semantics, planning, permissions, execution, readback, logs, status, and recovery;
  • CLI, Web console, MCP, and AI skills read the same deployment records.

That keeps local CLI and team CI from diverging. A developer can first connect the deploy path with CLI, then move the stable command into GitHub Actions. If CI fails, the local CLI or Web console can still inspect the same deployment attempt instead of forcing everyone to dig through Actions logs.

We Should Not Pretend This Is Free

Digest-first deployment adds work.

You need registry permissions, image retention, submodule checkout, config snapshots, provenance schema, artifact readback, runtime labels, rollback targets, and cleanup policy. For a weekend project, that may feel heavy. For teams, AI agents, pull request previews, hosted control planes, and production environments, it removes a lot of ambiguity.

Appaloft’s direction is not to hide every deployment detail behind a button. The goal is lighter entrypoints and harder evidence. GitHub Actions can trigger deployment, AI agents can help generate or call workflows, and humans can review state in CLI and Web. They should all work around the same facts.

A good deployment should leave more than the word “success.” It should leave enough information for you, six weeks later, to answer what was deployed, who triggered it, which config it used, which artifact the runtime actually ran, how it was verified, and how to undo it.