Architecture22 min read

Why Every Release You've Ever Published Can Be Silently Changed

A practical guide to permanent, verifiable, machine-readable software releases using Arweave, Radicle, IPFS, and a manifest format your CI pipeline and your agents can reason about.

A practical guide to permanent, verifiable, machine-readable software releases using Arweave, Radicle, IPFS, and a manifest format your CI pipeline and your agents can reason about.

For how this sits in the broader ClawQL vision — IDP pipeline, inference gateway, payments — see docs.clawql.com/vision/immutable-releases. This also pairs with Part 12 of Hardened Agentic Stack on supply-chain signing for admission-time Cosign/Kyverno enforcement.


The Incident That Made This Real

In December 2021, the colors and faker npm packages — with a combined weekly download count in the hundreds of millions — were deliberately sabotaged by their maintainer. The packages had been published once and trusted indefinitely by thousands of projects. One morning, anyone who ran npm install got corrupted code.

This wasn’t a compromised account or an external attacker. The maintainer had full, legitimate control. The platforms hosting your software artifacts were never designed to prevent the publisher from changing what was published. Tags can move. Images can be overwritten. Packages can be yanked and republished.

The threat model for software supply chains has shifted materially in the last three years. AI coding agents now autonomously pull dependencies, deploy services, and apply updates without a human reviewing each step. An agent that trusts a release because it was signed yesterday has no way to know the signed artifact was silently replaced this morning. The verification model that works for human engineers — check the changelog, read the diff, consult a colleague — doesn’t scale to autonomous deployment.

Supply chain attacks are no longer theoretical, either. The March 2026 LiteLLM compromise. The 2024 XZ Utils backdoor, embedded in a compression library shipped in almost every Linux distribution. The 2020 SolarWinds attack. Each succeeded because the artifacts being distributed were trusted implicitly — because they came from the right place, with the right signature, from an account that had been legitimate until it wasn’t.

This post describes a practical architecture for making releases permanent, tamper-evident, and machine-readable. It doesn’t require abandoning GitHub or your existing workflow. It requires one CLI tool and about thirty minutes for your first release.


What “Permanent” Actually Means

“Immutable” gets used loosely in software. A URL is a location. The file at that location can change while the URL stays the same. A content address — a hash of the file’s bytes — is a permanent identifier. If the content changes, the address changes. You can’t swap the file out and keep the same address. IPFS, Arweave, and git objects all share this property.

A Wayback Machine snapshot is content-addressed but can be taken down. A GitHub release can be deleted. Permanent storage means the economics and governance of the system make deletion effectively impossible, not just unlikely. Arweave’s endowment model — you pay once, the network is funded to store your data for roughly 200 years — is the only production system that comes close to this guarantee for arbitrary artifacts.

A signature from the publishing organization tells you the artifact was signed by someone with access to the signing key. It doesn’t tell you the key wasn’t compromised, or the signing process wasn’t automated without review, or the manifest wasn’t altered after signing. Verification needs to be independent of trusting the publisher.

A signed artifact tells a machine “this hasn’t been tampered with.” A manifest with a policy block tells a machine “here’s what to do with it, here’s what rollback looks like, here’s what signatures are required before deployment.” Agents need all of these — not just tamper evidence but structured instructions they can act on.


The Architecture

Four layers, each handling a distinct part of the problem. You can adopt them incrementally. They’re designed to compose.

LayerJobTechnology
1 CollaborationDay-to-day git, review, issuesRadicle (+ GitHub mirror)
2 Staging / buildsContent-addressed WIP + agent workspacesIPFS + Rift (CoW)
3 Official releasesPermanent canonical artifactsArweave
4 Release toolingBundle, publish, verify, pullclawql-release CLI

Layer 1: Collaboration

Day-to-day development happens on Radicle, a peer-to-peer Git-compatible collaboration network. Radicle gives every contributor a cryptographic identity, replicates repositories directly between peers without a central server, and has no single operator who can take the service down, rewrite history, or be compelled to delete your project.

GitHub stays in the picture as a read-only mirror. Your project is still discoverable on GitHub, still cloneable, still star-able. The mirror displays a banner pointing to the canonical Arweave source for anything that matters for verification.

A self-hosted Forgejo or Gitea instance is a real improvement over depending on GitHub specifically. It doesn’t eliminate the architectural vulnerability — whoever runs the server can still rewrite history, take it down, or have their infrastructure compromised. Radicle’s advantage is that it provides the collaboration layer and peer-to-peer replication with cryptographic identity, without a central operator.

The honest tradeoffs: Radicle is less mature than GitHub, most developers haven’t used it, and its long-term maintenance trajectory is worth watching. This is why Radicle sits at the collaboration layer, not the trust layer. The trust guarantees in this architecture live in Arweave’s content-addressed permanent storage and in cryptographic signatures. If you need to migrate the collaboration layer, your releases on Arweave remain completely unaffected.

For teams not ready for Radicle, starting with GitHub as the collaboration layer and adding Arweave for releases is a valid first step. Layer 1 is the most optional piece.

Layer 2: Staging and Build Environments

In-progress branches, CI build artifacts, and build environments live on IPFS — content-addressed storage where files are identified by a hash of their contents.

For build environments specifically, Rift uses copy-on-write filesystem features (btrfs on Linux, APFS on macOS) to create isolated, instantly-available workspaces:

git worktreeRift
Time to create a workspaceSeconds (full checkout)Under 0.1 seconds
Extra disk spaceHigh (full copy)Near-zero until files change
Many agents in parallelPainful — shared lock files, node_modules conflictsStraightforward
Build reproducibilityHard to guaranteeSnapshot ancestry recorded in manifest

Rift is early-stage and maintained by a separate project. If it doesn’t work for your setup, git worktree is always a valid fallback — everything downstream works with either. The --backend flag on the CLI makes this explicit.

When you run many AI agents in parallel, each working on a different task in an isolated environment, you need workspaces that spin up in milliseconds, consume near-zero disk until they diverge from the parent, and produce an auditable chain of parent-to-child snapshots. This is the same problem macOS snapshot-based backups and Kubernetes pod isolation solve for their respective domains. Rift brings it to developer workspaces.

Layer 3: Official Releases

When you tag a release, the CLI bundles everything that matters — source code, container images, Helm charts, a CycloneDX SBOM, Cosign signatures, and SLSA attestations — and uploads it once to Arweave. The resulting transaction ID becomes the permanent, canonical identifier for that release. Anyone can fetch that exact release from any Arweave gateway, indefinitely, and verify it matches what was originally published.

Cost:

Release contentsApproximate cost
Source + signatures only (~10 MB)~$0.07
Source + container image + SBOM (~100 MB)~$0.70
Full release with large images (~500 MB)~$3.50
Large monorepo bundle (~1 GB)~$7–10

For most projects, a release costs under a dollar.

S3 versioning keeps old versions but doesn’t prevent the account holder from deleting them. Your security shouldn’t depend on whether the hosting organization keeps paying its AWS bill or decides to remove your project. IPFS is content-addressed but doesn’t guarantee persistence — content is available as long as someone is pinning it. Pinning services are businesses that can shut down or be compelled to remove content. Arweave’s endowment model is different: you pay once, the endowment funds replication for the long term, and no single operator can delete what’s been published.

The honest caveat: the endowment model works as long as the cryptoeconomic assumptions hold over a very long time horizon. The practical comparison for most teams isn’t “Arweave vs. perfect permanent storage” — it’s “Arweave vs. GitHub releases that can be deleted.”

Layer 4: The Release CLI

clawql-release init
clawql-release immutable-volume snapshot [--backend rift|git-worktree|cloudflare|ebs]
clawql-release golden-image build
clawql-release publish --tag v2.1.0
clawql-release verify [tx-id]
clawql-release pull [--rift]

Open source, TypeScript, works with or without Rift.


The Release Manifest

Every upload to Arweave includes a manifest — a compact JSON document that turns a pile of binary artifacts into something a machine can reason about:

{
  "version": "2.1.0",
  "merkleRoot": "8f3a9b...",
  "artifacts": {
    "source": { "cid": "bafy...", "sha256": "a1b2..." },
    "image": { "cid": "bafy...", "sha256": "c3d4...", "ref": "myapp:2.1.0" },
    "sbom": { "cid": "bafy...", "format": "spdx-json" }
  },
  "buildEnvironment": {
    "type": "rift",
    "snapshotId": "snap_8821",
    "parentSnapshotId": "snap_8790",
    "createdAt": "2026-06-01T14:22:00Z"
  },
  "policy": {
    "requireSignatures": ["build-pipeline", "security-review"],
    "canaryPercent": 5,
    "blastRadiusCap": "single-region",
    "rollback": {
      "previousRelease": "tx_7f2a9c...",
      "trigger": "error_rate > 1% for 5min"
    }
  },
  "compatiblePolicyVersion": "1.0"
}

merkleRoot covers every artifact in the bundle. Change one byte of anything — source code, the image, the SBOM — and the root no longer matches. Anyone can verify this independently using only the transaction ID and standard cryptographic libraries. No API call to a verification service. No trust in ClawQL, Arweave, or any other operator.

This differs fundamentally from a publisher signature. A signature tells you the artifact was produced by someone with the signing key. A Merkle root tells you the artifact hasn’t changed since it was published, verifiable by anyone with the content.

buildEnvironment records exactly which build environment produced this release, with a link back to its parent. The chain is: parent snapshot → this snapshot → these artifacts → this Merkle root → this Arweave transaction ID. Each link is independently verifiable. Rift’s CoW workspaces become supply-chain evidence rather than a developer convenience.

policy is the piece that changes deployment for AI agents and automated pipelines. A Kubernetes admission controller running Kyverno or OPA Gatekeeper can read the policy block and refuse to deploy anything that doesn’t satisfy requireSignatures, automatically configure a canary rollout at canaryPercent, know exactly what to roll back to when rollback.trigger fires, and enforce blastRadiusCap before allowing multi-region deployment. An autonomous agent deploying this release reads the manifest and acts on structured instructions. Rollback isn’t “try to figure out what the previous version was” — it’s previousRelease: "tx_7f2a9c...", a permanent identifier pointing to a specific, verifiable state.

compatiblePolicyVersion versions the manifest schema publicly. Auditing a release means auditing against a known, stable schema rather than reverse-engineering an opaque format.


Private and Paid Releases

Public releases are uploaded unencrypted. Private or commercial releases add one step: the bundle is encrypted with XChaCha20-Poly1305 before upload, and the manifest declares which wallet address is authorized to decrypt. For more complex access — “decrypt after payment,” “decrypt for wallets that hold this token” — the manifest can reference Lit Protocol, which handles conditional decryption based on on-chain state.

The x402 payment flow for an AI agent consuming a paid release:

const manifest = await clawql.verify(txId);

if (manifest.access.paymentRequired) {
  const payment = await x402.pay({
    amount: manifest.access.price, // e.g. "0.50 USDC"
    recipient: manifest.access.wallet,
    resource: txId,
  });

  const decryptionKey = await lit.requestKey({
    condition: manifest.access.decryptCondition,
    proof: payment.receipt,
  });

  const bundle = await clawql.decrypt(txId, decryptionKey);
}

The agent pays, receives a key, and decrypts — one atomic flow, no human approval step for routine low-value transactions. Whether that’s appropriate without human approval is a policy decision configured in the agent’s authorization model, not something baked into the payment infrastructure.


Honest Failure Modes

An Arweave gateway is unreachable. Arweave data is replicated across many independent gateways. If one is down, clawql-release pull tries another. The transaction ID is a property of the network, not of any single server. In practice this is “occasionally slower,” not “occasionally unavailable.”

The AR token price changes dramatically. The endowment model is designed to fund storage even if the token’s value drops substantially. The real risk is that this is a cryptoeconomic assumption that needs to hold over decades. The practical mitigation is keeping artifact sizes modest — the manifest, signatures, and source code are what need permanence; large binary blobs can be re-derived from source plus a recorded build environment.

A compromised signing key publishes a malicious manifest. This is the most important failure mode. Suppose someone with valid signing credentials publishes a manifest that looks legitimate — correct signatures, valid Merkle root — but with a policy block designed to cause harm: a rollback.previousRelease pointing to an old vulnerable version, or a canaryPercent of 100 disguised as 5.

The defenses layer. The manifest schema is public and versioned, so an admission controller can validate that policy values are sane independent of whether signatures check out. policy.requireSignatures should require multiple independent signers for production releases — a build pipeline signature alone is necessary but not sufficient. Treat signing credentials with the same rigor as production deployment credentials. Signing keys belong in Vault or HSM-backed exchange patterns, not in long-lived CI environment variables. Because every release is permanent, a malicious release doesn’t erase legitimate history. The previous good release is still there, still verifiable, still pullable.

The Radicle ecosystem doesn’t pan out. This is a real risk. Radicle is less mature than GitHub and its long-term maintenance trajectory is uncertain. The architectural response: trust guarantees don’t live in Radicle. If you need to migrate the collaboration layer, your releases on Arweave are completely unaffected. The collaboration layer and the trust layer are deliberately separated.


Why AI Agents Make This Urgent

The supply chain verification problem existed before autonomous agents. Agents make it acute.

A human engineer encountering a suspicious release can pause, consult colleagues, check the changelog, compare with a known-good state. An agent deploying software has to operate on the information available in the release artifacts themselves. If that information doesn’t include policy, rollback instructions, and verifiable provenance, the agent either proceeds blindly or stops and waits for a human.

The manifest format described here is designed to be the information layer agents need. Not just “this artifact hasn’t been tampered with” — which Merkle roots and signatures provide — but “here’s what to do with it, here’s what rollback looks like, here’s what conditions should trigger rollback, here’s what signatures are required before you deploy this.”

The XZ Utils backdoor was embedded over years by a patient attacker who built reputation gradually before introducing the malicious change. A well-configured agent with access to this manifest format would have noticed: the signing key for the release differed from previous releases, the build environment snapshot had no ancestry chain, the policy block required signatures that weren’t present.

A sophisticated attacker who compromises the entire signing infrastructure can produce a valid manifest with valid signatures. The marginal attacker — who compromises one account, or pushes a dependency update that nobody will notice — has a much harder time in a world where admission controllers validate every manifest field before deployment.


The Release Workflow

# One-time setup
rift init
clawql-release init

# Day-to-day — unchanged from current workflow
rift create --name feature-42
cd .rifts/feature-42
# edit, test, commit as normal

# When ready to ship
clawql-release immutable-volume snapshot --backend rift --name build-2026-06-01
clawql-release golden-image build       # signed images, SBOMs, attestations
clawql-release publish --tag v2.1.0    # uploads to Arweave, updates GitHub mirror banner

# Consuming a release anywhere
clawql-release pull --rift              # verifies manifest + signatures, decrypts if needed
clawql-release verify tx_abc123         # verify any existing release by transaction ID

Teams not using Rift run the same commands with --backend git-worktree.


What Changes

Typical GitHub + CIThis architecture
Can a release be silently changed after publishing?Yes — tags can moveNo — Arweave transactions are permanent
Can you prove what build environment produced a release?Usually notYes — snapshot ancestry in the manifest
Can a Kubernetes cluster enforce deployment policy automatically?Only with custom toolingYes — manifest is machine-readable
Can an AI agent verify, purchase, and consume a release autonomously?Not designed for thisYes — first-class with x402 payment flow
Running many isolated build environments in parallelPainfulTrivial with Rift
Private and paid releasesRequires custom infrastructureBuilt in — XChaCha20 + Lit Protocol
How long does the release last?As long as the platform exists~200 years (Arweave endowment model)
Effort to adopt Layer 3 (releases)One CLI, thirty minutes, one dollar

Adoption Path

Nothing here requires an all-or-nothing migration.

Add clawql-release alongside your existing GitHub workflow first. Nothing about how you develop or review code changes.

Run clawql-release publish --tag v2.1.0 for your first release. It’s now permanent and verifiable. GitHub still works exactly as before. Your users won’t notice anything different, except that this release can never be silently altered and the manifest is machine-readable.

Adopt Rift for faster, isolated build environments when it makes sense — particularly useful if you’re running multiple AI agents in parallel. git worktree works indefinitely as the alternative.

Move day-to-day collaboration to Radicle when you want full peer-to-peer operation. Keep GitHub as a mirror for as long as you want.

The only required step for most teams is publishing the first release. Everything else is optional hardening you adopt when it solves a problem you actually have.


CI Pipeline Integration

For teams running GitHub Actions today, adding permanent releases is a single new job:

jobs:
  release:
    needs: [build, test, sign]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install clawql-release
        run: npm install -g clawql-release

      - name: Initialize release
        run: clawql-release init

      - name: Build golden image
        run: clawql-release golden-image build

      - name: Publish to Arweave
        run: clawql-release publish --tag ${{ github.ref_name }}
        env:
          AR_WALLET_KEY: ${{ secrets.ARWEAVE_WALLET_KEY }}
          COSIGN_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}

      - name: Output transaction ID
        run: echo "Release published at $ARWEAVE_TX_ID"

The transaction ID goes into your release notes, your changelog, and wherever you track canonical release identifiers.


The clawql-release CLI and manifest schema live in ClawQL. Full platform context: Immutable Releases.

About the author

Daniel Smith builds ClawQL, an agent operating system for token-efficient discovery and execution over APIs — with observability, hardened tool boundaries, and production routing for LLM workloads. He writes here about the systems problems behind shipping agents.