Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

liskov-github-actions

Reusable GitHub workflows + actions for Liskov CI, so each Liskov repo's CI is a short uses: …@v1 instead of copy-pasted YAML + scripts. Plan + decisions: liskov-agent-orchestrator BKLG-20260624-jn9l (extracted from liskov-diagnostic's inline scripts).

Reusable workflows (the one-liners)

acurast-app.yml — build → IPFS-pin → OIDC attest

# .github/workflows/<app>.yml in an Acurast app repo
on:
  push: { branches: [main], paths: ['<app-dir>/**'] }
  workflow_dispatch:
permissions: { id-token: write, contents: read }
jobs:
  artifact:
    uses: proof-computer/liskov-github-actions/.github/workflows/acurast-app.yml@v1
    with:
      app-id: uptime-prober
      working-directory: uptime-prober      # default "."
      entrypoint: app.cjs                    # default; use bundle.cjs + extra-files: app.cjs for a stage0 app
      authored-manifest-path: uptime-prober/.liskov/uptime-prober.policy.json
    secrets:
      ACURAST_IPFS_URL: ${{ secrets.ACURAST_IPFS_URL }}
      ACURAST_IPFS_API_KEY: ${{ secrets.ACURAST_IPFS_API_KEY }}

Runs pnpm install --frozen-lockfile → typecheck → test → build (optional smoke), pins the Acurast deploy zip to IPFS no-spend, and posts a GitHub-OIDC artifact pin bound to the exact authored and release-intent digests. Existing callers keep the generated-zip defaults with app-id, authored-manifest-path, working-directory, entrypoint, extra-files, node-version, smoke, attest, and pin-url. The workflow exposes the CID, digest, uploaded build-manifest path, artifact-version IDs, per-Application result JSON, and target count.

Callers that already prepare deploy bytes may set prepare-command and artifact-path. The path is relative to working-directory; ipfs-pin hashes and uploads those exact bytes without repacking them. artifact-metadata-path accepts a strict JSON sidecar containing only the currently supported Diagnostic evidence: optional devtools (enabled, injected, and uploadMode: "direct"), artifact (format, safe entrypoint, and restartPolicy), sourceBundleSha256, failure, and scenarioArtifact. Unknown fields and unsafe paths fail closed. OIDC claims, repository identity, refs, and workflow identity never come from this sidecar.

script-ipfs reuses an exact ipfs://CID without another upload while still hashing the local bytes. Set ipfs-gateway-url to a credential-free HTTPS URL (with optional {cid}) to require exact gateway-byte equality. Verification allows four propagation attempts with ten seconds between attempts. The build manifest is always uploaded as a GitHub Actions run artifact.

For one prepared artifact shared by multiple Applications, set artifact-targets-path instead of authored-manifest-path. It is a repository-root relative non-empty JSON array with no extra keys:

[
  {"applicationId":"diagnostic-a","authoredManifestPath":".liskov/diagnostic-a.json"},
  {"applicationId":"diagnostic-b","authoredManifestPath":".liskov/diagnostic-b.json"}
]

Each exact authored manifest must be V4, name the matching Application, and authorize an unencrypted ipfs_bundle build. The shared action mints one OIDC token, posts one manifest-bound pin per target, and retains the original single-target inputs and first artifact-version-id output for compatibility.

marketplace-ingest.yml — catalog OIDC push (ADR-0006 §A1)

on: { push: { branches: [main] } }
permissions: { id-token: write, contents: read }
jobs:
  publish:
    uses: proof-computer/liskov-github-actions/.github/workflows/marketplace-ingest.yml@v1

Mints an OIDC token and tells liskov-rs to reconcile the catalog to this commit. The /api/marketplace/ingest endpoint is not built yet (catalog schema spec, Part 2) — this is ready for it.

policy-sync.yml — authored manifest → imported draft

# .github/workflows/liskov-policy.yml in an app repo
on:
  push: { branches: [main], paths: ['.liskov/**'] }
permissions: { id-token: write, contents: read }
jobs:
  manifest:
    uses: proof-computer/liskov-github-actions/.github/workflows/policy-sync.yml@v1
    with:
      application-id: slipway-diagnostic
      manifest-path: .liskov/slipway-diagnostic.policy.json

Checks out the repo, mints an OIDC token, and POSTs the authored manifest to POST /api/applications/<id>/policy-imports/github. The server verifies the token, requires the Application to already be bound to THIS repository, pins the recorded source.commit from the verified sha claim (auto-imports are commit-pinned by construction), and imports a draft. Publication and artifact selection are separate.

runtime-image.yml — manifest import → scoped image upload → bootstrap

on:
  workflow_dispatch:
    inputs:
      image_url: { description: Pinned PRoot image URL, required: true, type: string }
      expected_sha256: { description: Optional SHA-256, required: false, type: string }
permissions: { id-token: write, contents: read }
jobs:
  runtime-image:
    uses: proof-computer/liskov-github-actions/.github/workflows/runtime-image.yml@v1
    with:
      application-id: my-app
      manifest-path: .liskov/my-app.json
      image-url: ${{ inputs.image_url }}
      expected-sha256: ${{ inputs.expected_sha256 }}
      attestation-repository: proof-computer/liskov-runtime-images
      attestation-source-digest: ${{ inputs.image_source_commit }}
      attestation-signer-workflow: proof-computer/liskov-runtime-images/.github/workflows/ci.yml

Downloads the supplied image and locally verifies its SHA-256 before any import or upload. When attestation-repository, attestation-source-digest, and attestation-signer-workflow are supplied, the workflow requires expected-sha256 and verifies the downloaded file's GitHub artifact attestation against that exact source commit and signer workflow before any state-changing step. The three attestation inputs are all-or-none. Omitting all three preserves the existing unattested direct-URL migration/debug path.

The workflow then imports the exact V4 runtime-image build manifest and passes the server-authoritative authoredDigest and releaseIntentDigest to a scoped upload action. Liskov verifies that exact binding before creating one-object Tigris credentials; the action uploads without exposing the credentials, remints OIDC, finalizes, and returns the image, bootstrap, artifact-version, cleanup, and safe provenance outputs. Set liskov-url to use the same custom Liskov base for both import and upload.

cargo-runtime-image.yml — locked Rust build → deterministic image → upload

This workflow is the reusable supply-chain path for native Cargo applications. It requires Cargo.lock, an exact rust-toolchain.toml channel, and an attested, digest-pinned helperless Liskov rootfs. On a native ARM64 runner it builds the selected binary twice for aarch64-unknown-linux-musl, rejects a dynamic interpreter, and requires identical binary bytes. It then overlays each build at the declared absolute path, normalizes the rootfs archive, rejects any embedded liskov-runtime-contact, and requires both complete images to have the same digest and bytes.

Only after that proof does the workflow optionally attest the generated image, import the exact authored manifest, and invoke the existing scoped upload/finalization action. Generated-image attestation defaults on. Set attest-runtime-image: false when GitHub artifact attestations are unavailable, including private repositories whose organization plan does not provide them. This skips only publication of GitHub build provenance for the derived image: Liskov still verifies the caller's GitHub OIDC identity and binds the exact Application, manifest digest pair, source commit, workflow, and image digest. The base image remains digest- and attestation-verified in both modes. GitHub requires the caller to grant every permission declared by a reusable workflow, so callers must retain attestations: write even when this input is false; the skipped step creates no attestation.

Callers must pin the base image's SHA-256, source commit, repository, and signer workflow. Release callers should use the maintained promoted rootfs; a release-candidate rootfs remains suitable only for an explicitly controlled canary.

permissions:
  attestations: write
  contents: read
  id-token: write

jobs:
  image:
    uses: proof-computer/liskov-github-actions/.github/workflows/cargo-runtime-image.yml@v1
    with:
      application-id: rust-hello-world
      manifest-path: .liskov/rust-hello-world.policy.json
      working-directory: rust-hello-world
      binary-name: rust-hello-world
      install-path: /usr/local/bin/rust-hello-world
      base-image-url: <immutable release asset URL>
      base-image-sha256: <64 lowercase hexadecimal characters>
      base-attestation-repository: proof-computer/liskov-runtime-images
      base-attestation-source-digest: <exact source commit>
      base-attestation-signer-workflow: proof-computer/liskov-runtime-images/.github/workflows/ci.yml
      attest-runtime-image: false # optional; defaults to true

À-la-carte actions

Compose your own job from these (uses: proof-computer/liskov-github-actions/actions/<name>@v1):

Action Kind Does
setup composite pnpm + Node + install --frozen-lockfile
acurast-build composite typecheck → test → build (+ optional smoke)
ipfs-pin JS generate a zip or read exact prepared bytes, optionally reuse/verify a CID, pin no-spend → cid/digest/manifest-path
artifact-pin-attest JS OIDC → one or more manifest-bound POST /api/applications/<id>/artifact-pins/github calls
marketplace-ingest JS OIDC → POST /api/marketplace/ingest
policy-import JS OIDC → POST /api/applications/<id>/policy-imports/github (import the repo's authored manifest as a draft)
runtime-image-upload JS Hash → manifest-bound scoped Tigris upload → fresh-OIDC finalize
cargo-runtime-image-build composite + Python Safely overlay one static AArch64 binary and emit a normalized helperless-rootfs-derived tar.xz

Versioning

  • Tag releases vX.Y.Z; release.yml moves the vX major tag so consumers pin @v1 and get the latest v1.x. Security-sensitive callers can pin a commit SHA.
  • v1.0.1 adds optional source-commit and signer-workflow verification for runtime images without changing existing direct-URL callers.
  • v1.0.2 adds the default-standard runtime-image bootstrap-mode input; the bridge-probe value remains restricted by Liskov to its exact internal canary workflow.
  • v1.2.0 adds backward-compatible caller-prepared artifact bytes, sanitized Diagnostic metadata, existing-CID gateway verification, multi-target artifact-pin attestation, reusable-workflow outputs, and durable build-manifest run artifacts.
  • v1.2.3 allows Cargo callers to skip publishing derived-image GitHub build provenance while retaining deterministic byte proof, base-image attestation, and Liskov's exact OIDC/manifest/image binding.
  • Reusable workflows reference their own JS actions by the literal @v1 major tag, so a caller pinned to @v1 executes the matching released action surface.

Security posture

Only no-spend (IPFS pin), OIDC attest/ingest, and scoped runtime-image upload operations are exposed; the spend-capable ACURAST_MNEMONIC CLI-upload path is intentionally not ported. Runtime-image sessions require the exact imported manifest digest pair, and upload credentials are masked, retained only in memory, scoped to one object, and never emitted as outputs. The attest/ingest/upload endpoints are gated server-side on repository, ref, workflow, and manifest authority.

Development

pnpm install
pnpm typecheck
pnpm build         # bundles each JS action src/ -> dist/index.cjs (committed)
pnpm check:dist    # build + fail if committed dist drifted (CI runs this)

actions/*/dist/index.cjs is committed (the node24 Actions runtime has no deps installed). Edit src/, run pnpm build, commit both.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages