Skip to content

Add git-launcher for pinned workload provenance#97

Open
h4x3rotab wants to merge 13 commits into
mainfrom
trusted-workload-launcher
Open

Add git-launcher for pinned workload provenance#97
h4x3rotab wants to merge 13 commits into
mainfrom
trusted-workload-launcher

Conversation

@h4x3rotab
Copy link
Copy Markdown
Contributor

@h4x3rotab h4x3rotab commented May 11, 2026

Summary

Adds git-launcher, a minimal dstack launcher for running workload code from an attested Git commit pin.

The design separates two provenance questions:

  • launcher image provenance: the OCI digest identifies the audited launcher implementation and is backed by GitHub/Sigstore build provenance;
  • workload code provenance: the attested launcher config selects REPO_URL + full COMMIT_SHA and the workload repo at that commit contains the entry script that installs, builds, and runs the app.

What changed

  • Adds git-launcher/bin/git-launcher, a small Bash launcher that parses config without sourcing it, rejects unknown keys, rejects branches/tags/short SHAs, checks out the configured commit, scrubs the checkout, and verifies HEAD before launch.
  • Defaults to reading /etc/git-launcher/config.conf when no config path is passed, so Docker Compose deployments can mount config at that path without setting command:.
  • Uses default mode for repos that contain entrypoint.sh; optional ENTRYPOINT_SCRIPT selects a different script path, and advanced RUN_CMD / INSTALL_CMD remains available for third-party repos that cannot carry an entry script.
  • Adds a digest-pinned runtime image definition and a git-launcher-v* release workflow that publishes docker.io/${DOCKERHUB_ORG}/git-launcher with GitHub/Sigstore build-provenance attestation.
  • Adds implementer-focused docs in git-launcher/README.md and verifier docs in git-launcher/VERIFY.md, including a 3-check default-mode quick path and reference-value comparisons for dstack attestation measurements.
  • Removes the placeholder sample config so users create a real config for the workload commit they intend to run.

Validation

Local checks at 14686aa:

  • ./git-launcher/tests/run-tests.sh -> 24/24 pass
  • git diff --check -> clean
  • YAML parse for .github/workflows/reproducible-build.yml and .github/workflows/git-launcher-release.yml -> ok

Release and deployment notes

Release by pushing a tag matching git-launcher-v*.

Downstream deployments should pin the launcher image by OCI digest, not tag. For production, bind the workload pin either through dstack-measured compose configs: mounted at /etc/git-launcher/config.conf, or through a derived image digest that bakes config into that path.

Comment thread README.md Outdated
|---------|-------------|
| [launcher](./launcher) | Generic launcher pattern for Docker Compose apps |
| [launcher](./launcher) | Generic launcher pattern for Docker Compose apps (auto-update) |
| [trusted-workload-launcher](./trusted-workload-launcher) | Tiny auditable launcher image that fetches a workload at a full upstream Git commit SHA — opposite trust posture to `launcher/` (no auto-update). The launcher image digest attests the launcher itself; the workload pin lives in a config file that must be attested via dstack `compose_hash`/`config_id` or baked into a derived image |
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too long

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Shortened to a single sentence (commit 3c44cce). Row now reads: Auditable launcher that pins a workload to a full upstream Git commit SHA (no auto-update). The longer trust-model discussion stays in the example's README/VERIFY.md.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still too lang. We need an one liner description. Not a compression of a paragraph.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Compressed to a true one-liner (commit b0478b6): Run a pinned Git commit in a TEE. Six words, fits the Details-table cadence. The longer trust-model discussion stays in trusted-workload-launcher/README.md and VERIFY.md.

Comment thread git-launcher/README.md Outdated
Comment thread git-launcher/bin/git-launcher
Comment thread trusted-workload-launcher/VERIFY.md Outdated
Comment thread git-launcher/VERIFY.md
Comment thread trusted-workload-launcher/VERIFY.md Outdated
Comment thread trusted-workload-launcher/VERIFY.md Outdated
Comment thread git-launcher/VERIFY.md
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, the verification process is too hard. Part of the reason is Intel TDX's complexity. Imagine with AWS Nitro Enclave and AMD SEV, users can just get the reference values of the image measurement from the build attested on Sigstore, and verify the attestation, and compare the reference values. But in our case, we have more than 7 steps. This will drive users away.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Restructured VERIFY.md to lead with a 5-step Quick path up front (commit 3c44cce): pull dstack attestation -> read attested compose (image digest + inline config) -> verify launcher Sigstore attestation -> audit upstream COMMIT_SHA -> spot-check logs. The detailed step-by-step CLI checklist now sits below as the deep path, with the TDX/event-log specifics scoped to step 2 where they are actually needed. A reader who already trusts the dstack/Phala verifier can stop after the quick path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify it even more? Give you some inspiration: Now we verify the launcher itself, and then read the config (git repo, install and run commands). What if we put the install and run command inside the git repo? So the cmd config verification step will be merged into the source code provenance.

This is just one example. We need to try hard to compress the verification steps even more from other angles.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Took your hint and folded install/run into source provenance by making it the default mode rather than a convention (commits b0478b6 + 331c3d0 for precision wording).

What changed runtime-wise:

  • INSTALL_CMD and RUN_CMD are now optional. In default mode the launcher runs bash tee-launch.sh from the pinned commit (under REPO_SUBDIR if set, else repo root). No exec bit on the file is required. INSTALL_CMD without RUN_CMD is rejected at validation. Advanced mode (RUN_CMD set, with optional INSTALL_CMD) remains for repos that cannot be modified.
  • Three new tests cover default-mode happy path, default-mode-with-missing-script (fail closed), and INSTALL_CMD without RUN_CMD. All 19 tests pass.

What that means for VERIFY.md:

  • Quick path is now 4 steps instead of 5: (1) dstack attestation -> launcher image digest + REPO_URL + COMMIT_SHA (+ REPO_SUBDIR if used), (2) Sigstore build-provenance attestation on the launcher image digest, (3) audit the upstream repo at COMMIT_SHA — which now also covers tee-launch.sh under the same source provenance, (4) spot-check runtime logs. The dedicated "extract and audit INSTALL_CMD/RUN_CMD" step disappears for default-mode deployments.
  • Advanced mode is documented as an explicit one-step extension: RUN_CMD / INSTALL_CMD are read out of the attested compose in step 1 and audited as trust-bearing deployment config (not as source at COMMIT_SHA, since they live in the launcher config rather than the upstream repo).
  • WORK_DIR is now called out as local plumbing (not trust-bearing); CHILD_ENV_FILE is called out as runtime configuration that affects behavior but not the bytes that run.

Convention chosen: fixed path tee-launch.sh (workload repo root, or REPO_SUBDIR/tee-launch.sh). The launcher invokes it with bash tee-launch.sh, so it does not need to be marked executable; this avoids the cross-platform exec-bit footgun. Failure to find it is a hard error — no fallback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's the comparison of the reference values vs the measurements in the attestation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Added a Reference values to compare block under step 2 of the deep checklist (commit 7304668), plus rewrote the quick-path step 1 to call out the comparison explicitly.

It enumerates exactly which attestation fields to compare and what to compare them against:

  • launcher image digest <-> the released digest you audited (Sigstore-verified in step 3)
  • compose-hash event <-> sha256(tcb_info.app_compose) of the compose you audited
  • mrtd and rtmr0/rtmr1/rtmr2 <-> the dstack OS-image reference values published with that dstack release
  • os-image-hash event <-> the dstack OS image hash you expect
  • app-id event <-> the on-chain dstack app contract / config ID (or the value you accept for KMS-less deployments)

Plus a one-shot shell snippet that pulls each from attestation.json via jq and asserts equality, so a reviewer can copy-paste it. rtmr3 is intentionally not treated as a single reference value: it is the running extension over the runtime event log (app-id, compose-hash, os-image-hash, instance bring-up events). The doc directs the verifier to either replay the event log into rtmr3 if their verifier supports it, or to check the constituent events individually as shown above. Reference values for the dstack OS image (mrtd / rtmr0..2) come from the dstack release the OS image was built from; that is the part Phala / dstack publishes alongside each release.

Two unrelated touches that landed in the same commit: (1) renamed the default entry-script convention to entrypoint.sh and added an optional ENTRYPOINT_SCRIPT config key, since aggregator workloads have adopted that name and the dstack-SDK pattern of talking to /var/run/dstack.sock; (2) all compose snippets now mount /var/run/dstack.sock into the workload container so the dstack SDK can reach the agent. Both are reflected in the verifier flow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Addressed in 67a101f by compressing the default-mode quick path to 3 checks: compare dstack attestation/reference values against the expected deployment, verify launcher image provenance via Sigstore, and audit the upstream repo at COMMIT_SHA. Runtime logs are now explicitly a smoke check, not a verification step, and default mode keeps install/run logic inside entrypoint.sh so command config does not add another default-mode verification step.

h4x3rotab added 5 commits May 11, 2026 18:05
- Shorten root README Details row
- Document line-oriented config and call-a-script pattern for multi-line
- Drop non-ASCII (em-dash) from launcher script comments and log line
- Rewrite VERIFY.md: lead with 5-step quick path, add Mermaid diagrams,
  drop TIP terminology, promote compose-mounted config as the recommended
  production path, rewrite source provenance around Sigstore attestation
  (not bit-for-bit reproducibility), and re-anchor the smoke transcript
  on compose-hash <-> sha256(app_compose) binding from a real production
  Phala smoke (CVM twl-cfg-smoke-20260511-180207, deleted after capture).
Drop "reproducibly built" claims outside VERIFY.md. The release workflow
publishes a Sigstore build-provenance attestation that binds the image
digest to a specific GitHub workflow run / repo / ref / SHA; that is a
signed chain of custody, not a guarantee of bit-for-bit reproducibility.

Updated README.md trust-model diagram and verifier list, and the
examples/web-app.conf header.
Make INSTALL_CMD and RUN_CMD optional and add a recommended default mode:
when neither is set the launcher runs 'bash tee-launch.sh' from the
pinned commit (under REPO_SUBDIR if set, else repo root). The script's
bytes are covered by source provenance of COMMIT_SHA, so the trust-
bearing config in default mode collapses to REPO_URL + COMMIT_SHA. The
existing RUN_CMD path is preserved as an advanced mode for repos that
cannot host their own entry script; INSTALL_CMD remains optional and
must accompany RUN_CMD.

Aligned changes:

- bin/trusted-workload-launcher: validate INSTALL_CMD requires RUN_CMD,
  drop "INSTALL_CMD must be set" and "RUN_CMD required" gates, add the
  default-mode branch that requires ./tee-launch.sh in target. No exec
  bit required (we invoke 'bash tee-launch.sh'). Refreshed top comment
  and parse_config comment to reflect the new model.
- tests/run-tests.sh: extended fixture repo with c3 (adds
  sub/tee-launch.sh, intentionally non-executable), added
  default_mode_happy, default_mode_missing_script_fails, and
  install_cmd_without_run_cmd_fails.
- README.md, examples/web-app.conf: lead with default mode; advanced
  mode explicitly scoped to "workload repo cannot host its own entry
  script"; clarified that no executable bit is required.
- VERIFY.md: quick path shortened to 4 steps for default mode, with an
  explicit one-line note that advanced mode adds the command audit;
  step 4 covers tee-launch.sh under source provenance and step 5 shows
  the default-mode log lines. Updated the recommended-path Mermaid
  diagram. Smoke transcript annotated that the prior production run
  used advanced mode because Hello-World ships no tee-launch.sh, and
  that the compose-hash binding it demonstrates is identical.
- Root README: Details-table description compressed to a true one-liner
  ("Run a pinned Git commit in a TEE.").
Three precision fixes to avoid overclaiming the simplification:

- Default mode: trust-bearing config is REPO_URL + COMMIT_SHA and
  REPO_SUBDIR when used (it selects which tee-launch.sh runs). Made
  this explicit in the example README intro, the default-mode section,
  the VERIFY.md quick-path summary and Mermaid diagram, and step 4 of
  the deep-path checklist. Clarified that WORK_DIR is local plumbing
  (not trust-bearing) and that CHILD_ENV_FILE / runtime env can affect
  runtime behavior but not the bytes that run, so it should be audited
  as runtime deployment configuration rather than as source.
- Advanced mode: corrected the framing so RUN_CMD / INSTALL_CMD are
  described as "trust-bearing deployment config" rather than "source
  code at the pinned commit" — they live in the launcher config, not
  in the upstream repo at COMMIT_SHA, and so are not covered by its
  source provenance.
- VERIFY.md log example: reordered to match what the launcher actually
  emits (mode line during the config summary, then checking out, then
  HEAD verified, then exec) and described the lines as ones the output
  should include, not a strict transcript.
reference-value comparison

Aggregator runtime now goes through the dstack SDK for KMS keys and TDX
quotes against /var/run/dstack.sock, so the launcher's docs/examples
need to (a) match the entry-script name workloads adopt and (b) make
the dstack agent socket visible inside the workload container. The
verifier-side review also asked for an explicit reference-values vs
attestation-measurements comparison.

Changes:

- bin/trusted-workload-launcher: rename default entry script to
  entrypoint.sh and accept a new optional ENTRYPOINT_SCRIPT key (relative
  path, validated against absolute paths and '..'). Default mode still
  runs `bash <entry>` with no exec-bit requirement. Updated usage/help
  text, top comments, parse_config comment, and the `mode:` / `entry:`
  log lines.
- tests/run-tests.sh: fixture's c3 now adds sub/entrypoint.sh and a
  sub/alt-entry.sh; added entrypoint_script_override and
  entrypoint_script_escape_rejected; updated default-mode marker
  assertions. 21/21 PASS.
- examples/web-app.conf: now describes entrypoint.sh as the convention
  and documents ENTRYPOINT_SCRIPT as an optional override.
- README.md (example): same convention; intro now lists
  `REPO_SUBDIR` / `ENTRYPOINT_SCRIPT` as the two path-selection trust-
  bearing fields. Added a `Mounting the dstack socket` subsection and
  added `/var/run/dstack.sock:/var/run/dstack.sock` to every compose
  snippet (local dev, production option A, and a note for option B).
  Workload-specific endpoint envs (e.g. DSTACK_LLM_ROUTER_DSTACK_ENDPOINT)
  are explicitly directed to CHILD_ENV_FILE, not to launcher config.
- VERIFY.md: added a `Reference values to compare` block under step 2
  with the concrete extraction shell snippet for `mrtd`, `rtmr0..2`,
  `compose-hash`, `os-image-hash`, `app-id`, and the launcher image
  digest inside the attested compose. Updated the quick-path step 1 to
  state explicitly that this comparison is what makes the attestation
  useful. Mentions of `REPO_SUBDIR` now also list `ENTRYPOINT_SCRIPT`.
- README.md (root): unchanged in this commit (still the one-liner from
  b0478b6).

No Rust-specific logic added to the launcher; it remains a build-system-
agnostic bash launcher. Removed nothing about ALLOW_INSECURE /
DSTACK_LLM_ROUTER_DSTACK_QUOTER_URL because neither was ever referenced
here (confirmed via grep).
@h4x3rotab h4x3rotab marked this pull request as ready for review May 20, 2026 05:23
@h4x3rotab h4x3rotab closed this May 20, 2026
@h4x3rotab h4x3rotab deleted the trusted-workload-launcher branch May 20, 2026 05:53
@h4x3rotab h4x3rotab restored the trusted-workload-launcher branch May 20, 2026 05:54
@h4x3rotab h4x3rotab reopened this May 20, 2026
@h4x3rotab h4x3rotab changed the title Add trusted workload launcher example Add git-launcher example May 20, 2026
Comment thread git-launcher/VERIFY.md
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's the comparison of the reference values vs the measurements in the attestation?

Comment thread git-launcher/VERIFY.md
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Added a Reference values to compare block under step 2 of the deep checklist (commit 7304668), plus rewrote the quick-path step 1 to call out the comparison explicitly.

It enumerates exactly which attestation fields to compare and what to compare them against:

  • launcher image digest <-> the released digest you audited (Sigstore-verified in step 3)
  • compose-hash event <-> sha256(tcb_info.app_compose) of the compose you audited
  • mrtd and rtmr0/rtmr1/rtmr2 <-> the dstack OS-image reference values published with that dstack release
  • os-image-hash event <-> the dstack OS image hash you expect
  • app-id event <-> the on-chain dstack app contract / config ID (or the value you accept for KMS-less deployments)

Plus a one-shot shell snippet that pulls each from attestation.json via jq and asserts equality, so a reviewer can copy-paste it. rtmr3 is intentionally not treated as a single reference value: it is the running extension over the runtime event log (app-id, compose-hash, os-image-hash, instance bring-up events). The doc directs the verifier to either replay the event log into rtmr3 if their verifier supports it, or to check the constituent events individually as shown above. Reference values for the dstack OS image (mrtd / rtmr0..2) come from the dstack release the OS image was built from; that is the part Phala / dstack publishes alongside each release.

Two unrelated touches that landed in the same commit: (1) renamed the default entry-script convention to entrypoint.sh and added an optional ENTRYPOINT_SCRIPT config key, since aggregator workloads have adopted that name and the dstack-SDK pattern of talking to /var/run/dstack.sock; (2) all compose snippets now mount /var/run/dstack.sock into the workload container so the dstack SDK can reach the agent. Both are reflected in the verifier flow.

Comment thread .github/workflows/reproducible-build.yml
Comment thread README.md Outdated
|---------|-------------|
| [launcher](./launcher) | Generic launcher pattern for Docker Compose apps |
| [launcher](./launcher) | Generic launcher pattern for Docker Compose apps (auto-update) |
| [git-launcher](./git-launcher) | Run a pinned Git commit in a TEE. |
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address code provenance

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Addressed in 67a101f. Updated the top-level README row to make the code-provenance purpose explicit: Bind TEE workload code provenance to an attested Git commit pin.

Comment thread git-launcher/examples/web-app.conf Outdated
@@ -0,0 +1,47 @@
# Example: pin a small web app at a specific upstream commit and run it
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of this file?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Addressed in 67a101f by deleting git-launcher/examples/web-app.conf. It was only a placeholder config and duplicated the README examples; the README now tells users to create a real config.conf for the workload commit they intend to run.

@h4x3rotab h4x3rotab changed the title Add git-launcher example Add git-launcher for pinned workload provenance May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant