Build and attest with GitHub Actions
The reusable workflow turns an allowed GitHub commit into immutable artifact evidence. GitHub OpenID Connect (OIDC) gives Liskov a short-lived statement of repository, ref, commit, and workflow identity. You do not store a Liskov bearer token or spend-capable credential in GitHub.
The moving v2 release is live. The production acceptance recorded for the
v1 contract used v1.2.2. Callers should use @v2 to receive compatible
fixes. Pinning a commit or an older v2.x.y tag does not freeze the run:
the workflow calls its actions through the moving v2 tag.
v2 moved to v2.3.1 on 27 October 2026. From v2.3.0, acurast-app.yml uploads your
bundle through the Liskov IPFS service by default and requires the
organization-id input. A caller that uses @v2 without that input fails
until it adds it. A caller pinned to an earlier release cannot add it: move it
to @v2 with organization-id, or set ipfs-endpoint to the Acurast IPFS
proxy, https://ipfs-proxy.acurast.prod.gke.papers.tech.
Add the caller workflow
name: Build Liskov Application
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
artifact:
uses: proof-computer/liskov-github-actions/.github/workflows/acurast-app.yml@v2
with:
organization-id: YOUR_ORGANIZATION_ID
app-id: status-worker
working-directory: .
entrypoint: app.cjs
authored-manifest-path: .liskov/application-manifest.json
Use the same Application ID, repository, ref, workflow path, and manifest path
as the manifest's builder block. In a monorepo, set working-directory to the
directory containing package.json and pnpm-lock.yaml.
Set organization-id to your Liskov organization ID. proof liskov whoami
prints it as Name (ORGANIZATION_ID, slug, role …). The ID is not a secret: it
selects the organization and grants no permission.
The default upload needs no repository secret. The workflow uses its GitHub
OIDC identity to ask Liskov for a single-use upload token, uploads the bundle to
PROOF's IPFS service, and reads the bytes back through PROOF's gateway before it
attests them. An artifact may be at most 8 MiB; a larger one is refused with
artifact_too_large.
To use the Acurast IPFS proxy instead, set the ipfs-endpoint input to
https://ipfs-proxy.acurast.prod.gke.papers.tech; it is not a secret. If that endpoint needs a key, pass it as the LISKOV_IPFS_API_KEY
secret. Neither path authorizes Acurast spend or Liskov policy publication.
What runs
The called workflow:
- checks out the triggering commit;
- installs the requested pnpm and Node.js versions;
- runs
pnpm install --frozen-lockfile,typecheck,test, andbuild; - packages the entrypoint and requested extra files;
- uploads the bundle through the Liskov IPFS service with a single-use token, without spending, and verifies it through PROOF's gateway; and
- attests the CID, SHA-256 digest, manifest digests, and GitHub OIDC identity to Liskov.
The Attest artifact pin step reports Liskov's deterministic
artifact-version-id. Record that ID for publication.
Verify the run
Open the GitHub Actions run and confirm:
- the workflow came from the allowed branch and exact commit;
- install, typecheck, tests, and build passed;
- the pinned CID and digest are present;
- artifact attestation succeeded; and
- an artifact-version ID was returned.
Then compare that evidence in Liskov:
proof liskov application artifact-pin list status-worker --json
This advanced read is safe. The workflow does not import or publish your manifest, select a deployment schedule, reserve Service Credits, or register an Acurast job. Continue with Validate, import, and publish.
Encrypted JavaScript release boundary
Encrypted JavaScript payload execution is production-verified with Actions
v1.3.2 and runtime SDK 0.3.30: the released workflow encrypted, pinned and
attested the module, and a processor obtained its managed key, loaded it and
reported application completion. General customer availability still requires
the registered V5 source-publication release in the
capability matrix.
The encrypted JavaScript recipe records the exact inputs, module contract, paused key setup and verification steps. It uses the existing managed secrets boundary and does not grant a build workflow publication or spending authority. See Trust and data boundaries before making a private-code claim; Cargo image and cache confidentiality remains separate.
Runtime-image workflows are internal
The same repository also releases runtime-image.yml and
cargo-runtime-image.yml, which upload or build a native runtime image. A
customer-authored Cargo or runtime image is Internal in the
capability matrix. This section records the
released contract so the version boundary is exact; it is not a supported
recipe.
From v2.2.0, both workflows at @v2 read the manifest-path input as a
retained Manifest V5 source document and import
nothing:
- the file is JSON at a repository-relative path, with no leading
/and no empty,., or..segment; - its
applicationIdequals theapplication-idinput,release.modeissource, andruntime.kindisnative_image; and - the Application already carries a source binding for the calling repository, ref, workflow, and manifest path.
A Manifest V4 at that path is refused before anything is uploaded. The frozen
@v1 release (v1.3.2) is historical: its V4 import is now refused by Liskov,
so it is not a fallback. New V4 imports and publications are closed on both
release lines. The @v2 internal contract requires a V5 document and its source
binding.
Finalizing an upload does not publish a policy version. Both workflows return
the build evidence a release.mode: source publication names, in the outputs
artifact-digest, source-repository, source-ref, source-commit,
source-workflow-identity, binding-revision, and revocation-epoch.
cargo-runtime-image.yml also takes the optional source-assurance input,
default false. When it is true the workflow verifies the committed
CycloneDX SBOM, generated from pnpm-lock.yaml when one is present and
otherwise from Cargo.lock, and returns source-digest and sbom-path. Both
outputs are empty when the input is omitted or false.
No @v2 runtime-image run has been observed in production, so this contract
is released but not accepted on a customer path.