MGS1026: a cacheable target reaches for a credential
A target reaches for a credential (magus\secret.read or magus\secret.endpoint) and
is still cacheable:
[fail] cacheable secret reads: 1 target(s) read a credential but may replay from cache,
so a rotated or revoked credential invalidates nothing and the target reports success
without authenticating; declare skip_cache with a reason
.: target "spell-publish" reads a secret and is cacheable (magusfile.buzz)
Why this is a failure you would not otherwise see
A resolved credential contributes nothing to the cache key. That is deliberate: hashing one would write the credential into cache metadata and partition your cache per rotation. The consequence is that rotating or revoking a credential invalidates nothing.
For most targets that is harmless. For a target that publishes with the credential it is not: its sources rarely change between runs, so it becomes a cache hit that never contacts the provider, never pushes, and reports success.
Nothing fails there, or later. The release goes out without what that target should have published, on a pipeline whose publish step is green.
The fix
Declare the target uncacheable, with the reason:
import "magus";
magus\project({
"targets": {
"spell-publish": {"skip_cache": "pushes to a registry per invocation; a replay would publish nothing"},
},
})
skip_cache takes a reason string rather than a boolean on purpose, and this is the case it
was written for: six months from now the reason is what tells a reader the target is
uncacheable by decision rather than by accident.
The same applies to any target whose output is a function of a credential: a signed artifact, a fetch from a private registry, a deploy. The test is simple: if revoking the credential should change what the target produces, the target cannot be replayable.
When to ignore it
This is a warning, not a load error, because a target can legitimately read a credential and
still produce a cacheable artifact: fetching a dependency whose content is addressed by hash,
for instance, where the credential authorizes the fetch but does not determine the result. Only
the author knows. Declaring skip_cache with a reason is still the cheaper answer unless the
target is expensive and provably content-addressed.
What this check cannot see
It is a static read of the target body, so it finds a direct magus\secret.read call. A
read behind a helper the static walk cannot reach is invisible to it, exactly like an unreached
ctx.readsFiles (MGS1004). It under-reports rather than over-reports, which is
the right direction for a check whose remedy is to opt a target out of the cache.
It also does not see Command.secrets on a spell op, which resolves at spawn rather than in a
target body.
See also
- Secrets: why a credential is not in the cache key
- Cache: what the key does hash
- MGS1004: the sibling check for footprint declarations the walk cannot reach
An endpoint carries this in a sharper form
magus\secret.endpoint counts here too, and the hazard is worse than for a read rather
than milder.
With a read, the credential at least passes through the magusfile, so an author has some chance of noticing the target never ran. With an endpoint the value never appears at all: the file names a reference and a host, and magus attaches the credential on the way upstream. So changing that reference (staging to production, a rotated vault path, a different service account) alters nothing the cache can see. The sources are identical, the target replays, and the output it replays was produced with the other credential.
That is a wrong build rather than a stale login, which is why the fix is the same and the reason to apply it is stronger:
ctx.skip_cache("reaches for a credential, which contributes nothing to the cache key");