Repository navigation
aster-srv brokers the shared artifact cache: manifests in Postgres, blobs in GCS via signed URLs - #159
Merged
calvin-archastro merged 3 commits intoOct 19, 2026
Conversation
Contributor
Author
|
ArchDev is watching this pull request. State: settled (cycle 4 of 4 for head e664829).
Paused by the |
calvin-archastro
force-pushed
the
factory/tsk_034hnGndkZOrIB2eE3wuJv-12b71685-70ab-4512-9c91-
branch
from
October 18, 2026 12:16
fa48d5e to
2257443
Compare
…stgres, blobs in GCS via signed URLs
… abandoned uploads, no plain-HTTP metadata call
…tention against commits
calvin-archastro
force-pushed
the
factory/tsk_034hnGndkZOrIB2eE3wuJv-12b71685-70ab-4512-9c91-
branch
from
October 18, 2026 18:57
2257443 to
e664829
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Epic: Aster restores real build artifacts from a shared cache, including the GitHub Actions cache
Task: aster-srv brokers the shared artifact cache: manifests in Postgres, blobs in GCS via signed URLs
Local git hooks skipped: pre-push on the submission push and the publish push. CI is the gate for what those hooks check.
Problem and author intent
aster-srv already authenticated GitHub Actions OIDC tokens and IAP viewers, but it did not broker a shared build-artifact cache. Without a gateway, each machine would need bucket credentials or a separate remote cache, and nothing on the server could authorize writes or expire entries.
The intent is for aster-srv to be that gateway. Manifests stay in Postgres so the server can authorize and expire them. Blob bytes stay in GCS under
cas/<sha256>. Clients never receive a key file; they upload and download through 15-minute V4 signed URLs that the pod signs with IAM CredentialssignBlobas its own Workload Identity service account.Before this change, a CI token was repository-scoped only. There was no workflow
refon the identity, no writer allowlist, and no cache routes, so a pull-request workflow could not be refused a cache write because no cache write existed. After this change, lookup is available to an authenticated CI token for its own repository and to an allowlisted IAP viewer. Upload and commit require a static development token, or an OIDC token whoserefmatchesASTER_SRV_CACHE_WRITERS(default*=refs/heads/<default branch>). A pull-request ref such asrefs/pull/N/mergedoes not match that default, so the server returns 403 and a branch cannot replace a stored key. An existing key is insert-only (ON CONFLICT DO NOTHING→ 409). A manifest that names a blob GCS does not have is refused with 422 and is not stored.What changed
cacheschema migration:cache.manifests(repo, key, manifest bytes, size,last_used_at) andcache.blobs(sha256, size,last_used_at).POST /v1/cache/lookupaccepts up to 256 keys. Unknown keys are omitted fromhits, not errors. A hit returns the manifest as base64 and a signed GET only for each schema-1 outputdigestorlogwhosecas/<sha256>object exists. Lookup refresheslast_used_aton the manifest and on blobs that exist.POST /v1/cache/upload(writers only) accepts up to 1024 sha256 digests and returns signed PUT URLs for objects not already in the bucket. If no bucket is configured, upload returns 503.POST /v1/cache/commit(writers only) decodes a base64 schema-1 manifest, requires every named blob to exist, then inserts the manifest and blob rows in one transaction. A second commit of the same key is 409. A named blob missing from GCS is 422 with amissinglist. A stalecache.blobsrow whose object is gone is deleted so a later upload can replace it.GOOG4-RSA-SHA256, 900-second expiry,UNSIGNED-PAYLOAD) and signs it viasignBlob. The URL keeps path slashes; only the string-to-sign percent-encodes segments. Tests injectFixedSignerand an in-memory bucket so no private key is loaded.mainand repeated every six hours, takes a Postgres advisory lock so one replica runs. It deletes manifests unused forASTER_SRV_CACHE_DAYS(default 14), then blob rows no remaining manifest names, and deletes those GCS objects before committing the row deletes. A failed object delete rolls the rows back.ASTER_SRV_CACHE_BUCKET(unset disables the store),ASTER_SRV_CACHE_DAYS,ASTER_SRV_CACHE_WRITERS(owner/name=refor*=ref; a pattern ending in*is a prefix match). OIDCrefis stored onIdentity; static tokens are markedstatic_tokenand may write. IAP viewers may look up an allowlisted repo only, and only ifASTER_SRV_UI_EMAIL_DOMAINSallows the email.Scope
ArchAstro/aster2e006138a093e5b1465689a0e0a80cbaf8e1b3df→0ea8faaf2aa92e11b5f8247f025c26eb4fd9e657. Seventeen paths, 41 hunks, +1890 / −13. The revision file list is complete (omitted_paths0,omitted_hunks0). CI log excerpts in the supplied checks are truncated.Touched:
Cargo.lock,crates/aster-srv/Cargo.toml(base64,sha2),crates/aster-srv/README.md,src/auth.rs,src/cache/{mod,policy,routes,signing,store}.rs,src/config.rs,src/http.rs,src/iap.rs,src/lib.rs,src/main.rs,src/test_results/durations.rs,tests/cache.rs,tests/common/mod.rs.Not in this revision: the client
CacheBackendthat would call these routes, and no workflow file change is included.Risk assessment
Medium. This is a new shared write path. A too-broad
ASTER_SRV_CACHE_WRITERSpattern, or treating a missingrefas the default branch, would let a non-default workflow upload and commit. The implementation denies a missing ref, matches the default branch exactly unless the pattern ends in*, and binds a CI bearer token to its own repository. IAP callers cannot write. Keys are not overwritten, and blobs are content-addressed, so a substituted object fails the sha256 name. Residual risks:IamSignerkeeps one access token in aOnceCellwith no refresh shown, sosignBlobcan fail after that token expires until process restart; commit recordscache.blobs.size_bytesas 0 rather than the object size; lookup omits a blob missing from GCS but does not delete that row (deletion of a stale row happens on upload/commit presence checks and on retention). Retention deletes objects before the transaction commits; a crash after delete and before commit leaves rows that the next pass can retry, because a missing object delete is success.User impact
Operators must set
ASTER_SRV_CACHE_BUCKETor the cache stays disabled. The pod identity needs object access on that bucket andiam.serviceAccounts.signBlobon itself. CI on the default branch (or a configured writer ref) can upload and commit; other refs, including pull requests, can look up but not write. Static development tokens may write. IAP viewers in an allowed email domain can look up allowlisted repos and cannot write. Signed URLs last 15 minutes. Unused manifests and unreferenced blobs age out after 14 days unlessASTER_SRV_CACHE_DAYSis set. Clients must speak the new/v1/cacheJSON routes; existing test-results routes are unchanged aside from extra identity fields.Testing
Canonical integration tests are defined in
crates/aster-srv/tests/cache.rsand skip unlessASTER_SRV_TEST_DATABASE_URLis set:unknown_key_is_a_miss,upload_commit_lookup_round_trip,commit_refuses_missing_blob,lookup_omits_a_blob_missing_from_gcs,policy_matrix_pr_oidc_forbidden_iap_reads_only,signed_urls_expire_and_no_key_is_stored,retention_drops_expired_rows_and_objects. They use real Postgres when that URL is set and an in-memory bucket withFixedSigner, not live GCS.crates/aster-srv/src/config.rstests assert the default bucket is unset, retention is 14 days, and the default writer allowlist acceptsrefs/heads/mainand rejects a pull-request ref and a missing ref. The function name for that config test is not in the excerpt.Pipeline checks
format,lint, andtestare reported succeeded with exit code 0. Those excerpts are truncated. The visible test excerpt shows other aster-srv tests and does not nametests/cache.rs, so this evidence does not prove the cache integration tests ran rather than skipped. A greencargo testwithout the database URL would still skip them. No workflow file in this revision shows that the database URL is set in CI. Patch contents are not proof those tests passed.Follow-ups and known issues
signBlob; the signer is initialized once.size_bytes = 0; only the test helperBlobStore::putrecords the real length.cache.blobsrow until a presence check or retention.ASTER_SRV_TEST_DATABASE_URLand thattests/cache.rsexecuted; the supplied test log is truncated and does not show that.signBlobIAM are not exercised by the in-memory signer.