magus v0.4.3 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
3 min read

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");
MGS1026magusfilesecretscacheskip_cachecredentialsreplay
Last updated (895d94e4)
Earlier changes on this page (4)

Full history ↗ · Blame source ↗

Glossary

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Service

A long-running or shared process magus manages across runs, distinct from a one-shot target. See services.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs: magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Invocation

One magus process's recorded facts (the targets it finished, their outcomes, the lease it acted as, and the session it ran in when a host delivered one), kept in a repo-scoped store every worktree shares. magus session lists them; the store prunes itself by last-fact age.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate.

Lease

The grant a holder takes on a job: the write and read paths that job declared, enforced in the checkout that took it with magus job exec. A job is the piece of work; a lease is permission over it.

Conventions

This page uses none of the site's convention markers. The full set is on the conventions page.