Skip to content

Latest commit

 

History

151 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

YueLink public build mirror

This repository contains no application source. Its workflow is generated from the private onesyue/yuelink release workflow by sync-build.sh; each public signed annotated tag carries an ATTESTED_SOURCE_COMMIT=<40hex> and SOURCE_ATTESTATION_RUN_ID=<id> machine binding. The public build checks out the exact builder revision's shared verify-source-attestation.sh, then independently authenticates the referenced run identity, exact proof artifact, full gate closure and GitHub provenance before it checks out that private commit. It refuses any BUILT_SOURCE_COMMIT mismatch. A same-named private tag is release-input validation, not build authority.

Stable releases are fail-closed on the Android upgrade key, immutable R2 credentials, and private-source deploy key before a public tag is created. Apple Developer ID/notarization and Windows Authenticode are optional for direct sideload distribution: each platform publishes its exact signing mode and install notice. Missing or failed platforms never borrow an older binary, and a partial release cannot produce or promote an updater manifest.

The public workflow publishes every successful current-tag platform independently with checksums, provenance, source-commit identity and truthful sideload notices. Only a complete five-platform / nine-artifact set can create an unsigned updater-manifest candidate. The updater signing seed remains in the private signing plane, which authenticates the current signed root, signs the candidate, enforces a monotonic version transition, publishes the signed archive/root, and verifies the live artifacts.

Since 2026-09-03 that promotion runs inside the public build (release job, last step Sign root manifest and promote (in-pipeline)scripts/ci/promote_in_pipeline.sh): a green stable build moves the signed root itself. The private signer workflow and the local/bastion wrapper remain only as fallback.

The independent public manifest watchdog also keeps a reviewed, signed minimum root at tests/fixtures/update-manifest-v1.json. It rejects a live root whose semantic version or publishedAt predates that floor even when the old root's Ed25519 signature and expiry remain valid. After every stable promotion, copy the exact signed root into that fixture in a reviewed follow-up and retain the previous root as a replay regression fixture. Never discover the floor from the CDN being authenticated. This ratchet does not remove retained installers for manual recovery; publishing a decreasing updater root is already forbidden by the private signer's monotonic transition contract.

From a clean, up-to-date master checkout:

./sync-build.sh ../yuelink
git diff -- .github/workflows/build.yml
./sync-build.sh --check ../yuelink

# Before the private source tag is cut, attest its exact 40-character master
# commit on public runners. This is the canonical source gate when private
# Actions cannot start because of billing; local test output is never accepted.
SOURCE_SHA=$(git -C ../yuelink rev-parse HEAD)
gh workflow run source-attestation.yml -R onesyue/yuelink-ci \
  -f source_sha="$SOURCE_SHA"
# Wait for every source gate and the final provenance job to succeed.

# Then create/push the signed private source tag and mirror it. release.sh downloads
# the proof, checks source/tag/builder/run/gate identity, and verifies its
# GitHub artifact attestation before it can create the public tag.
./release.sh vX.Y.Z

release.sh verifies the private tag is a GitHub-verified signed annotated release input, the generated workflow exactly matches that source commit, every mandatory sideload secret is configured, and a successful source attestation from the exact current yuelink-ci workflow commit binds all canonical gates to the private tag's peeled commit. The builder commit itself must be signed and GitHub-verified before the script can create a signed annotated public tag. The remote tag namespace is handled idempotently: an existing name succeeds only when it is annotated, peels to the exact builder commit and has a GitHub verified/valid signature; an uncertain push is followed by a fresh remote read. New and existing tags are both polled for GitHub verification, and the script never deletes, overwrites or re-tags a failed immutable name. Before any push, a retained local annotated tag must also match the complete expected annotation bytes; matching only the builder commit is insufficient. Both repositories therefore authenticate the selected source and builder revisions instead of relying on an unsigned ref name or a successful push exit status.

Every object below the R2 v prefix is now retained permanently under the live, enabled Cloudflare indefinite bucket-lock rule yuelink-release-versioned-indefinite (prefix=v). The historical prune-r2.yml filename now runs a read-only archive/lock audit; it contains no delete or overwrite path. There is no implicitly cleanable namespace. Any future ephemeral area must have a separate explicit prefix, age and ownership contract outside both locked v and security/ prefixes.

Immediately before private promotion, r2-lock-attestation.yml reads that same live rule with the existing configuration-read-only Cloudflare token and emits a 30-minute proof bound to the exact stable version, private source, candidate SHA-256, candidate builder/run and a fresh 32-byte random challenge. The proof receives GitHub-hosted artifact provenance; it contains no Cloudflare token, R2 write key or updater signing seed. The proof workflow commit must be the exact same signed master commit named by the candidate's builder tag/run; a later control-plane commit is never accepted. The private promoter also pins the workflow identity and rechecks Sigstore provenance and expiry before immutable archive publication and again immediately before the root CAS. This is a short-lived point-in-time attestation rather than a second live Cloudflare query; promotion starts immediately and an expired proof is replaced, never extended or accepted.

challenge=$(openssl rand -hex 32)
gh workflow run r2-lock-attestation.yml -R onesyue/yuelink-ci \
  -f version=X.Y.Z -f source_commit="$SOURCE_SHA" \
  -f candidate_sha256="$CANDIDATE_SHA256" \
  -f builder_commit="$BUILDER_SHA" -f build_run_id="$BUILD_RUN_ID" \
  -f challenge="$challenge"

The source attestation is equal to or stronger than the private source jobs: it checks master ancestry, the complete unreleased Dart format delta, Android release-signing contract, Flutter analysis, architecture imports, CocoaPods residue, workflow policy, the full Flutter suite with a reviewed floor of 2044 tests, the release security scanner, Wintun hashes, release metadata and manifest schema, full-history Gitleaks, core and service production-target govulncheck, macOS integration tests, and the Windows durability probe. Before release tags, Windows also builds the native DLL, packages it as ZIP and runs three complete startup/traffic/shutdown cycles with required engine leases. Real listening and closed loopback sockets verify the refusal/rebind check; a timeout never counts as a released port. These 17 gates bind the exact source. The final JSON proof is uploaded under the exact source SHA and receives GitHub build-provenance attestation. Updater signing material is never copied into Git. It remains in protected repository Secrets; the protected promotion step verifies the real key and remains independently mandatory. A manually dispatched recovery workflow may read those same canonical Android/updater Secrets, verifies both public signing identities, and emits only a two-recipient age ciphertext to the recovery bucket plus a one-day encrypted artifact. It has no pull-request trigger and no plaintext upload path.

Every external uses: reference in all public workflows is pinned to a full 40-character commit SHA. The repository-level Actions policy enforces that pinning, and .github/dependabot.yml maintains those immutable pins with reviewed weekly pull requests. Local or dynamically resolved actions are intentionally absent; adding one requires revisiting the repository policy before it can merge.

The live repository policy was read back on 2026-08-21 as allowed_actions=selected, sha_pinning_required=true, github_owned_allowed=true, and verified_allowed=false. GitHub-owned actions are covered by that dedicated switch; the selected external patterns are the following exact closed set, with no unused allowance:

android-actions/setup-android@*
onesyue/yuelink-ci@*

The contract inventories all eight workflow files plus the composite Flutter action (nine action-bearing YAML definitions), checks every uses: against this policy, and rejects mutable refs, an unknown external repository, or a stale/extra documented pattern.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages