credentials.sh: gitea-mosaicstack reads a flat .token path that a multi-identity layout never has — unusable, and it masquerades as missing access #947

Open
opened 2026-07-30 13:20:30 +00:00 by Mos · 4 comments

Summary

load_credentials gitea-mosaicstack reads:

GITEA_TOKEN="$(_mosaic_read_cred '.gitea.mosaicstack.token')"   # credentials.sh:186

A flat .token under the instance. But a multi-identity layout nests one object per identity:

.gitea.<instance>.url
.gitea.<instance>.default        -> name of the identity slot to use
.gitea.<instance>.<slot>.token   <- the token actually lives HERE
.gitea.<instance>.<slot>.username

So the flat path resolves to nothing and gitea-mosaicstack can never succeed against such a layout. The single-identity sibling (gitea-usc, flat .token) works, which is why the defect is instance-specific and easy to miss.

What it does correctly

It fails closed: line 189 emits Error: gitea.mosaicstack.token not found and returns 1. That part is right and should not change.

Why it is worth fixing anyway

The failure is indistinguishable from not having access at all. Reads against a public repo keep working anonymously, so:

  • every read succeeds and looks authenticated,
  • the first write returns 401 token is required,
  • and the natural conclusion is "I lack access to this forge" rather than "the loader looked in the wrong place".

In this incident that produced a standing blocked-on-access report with the wrong cause recorded. The credentials existed and carried admin=true the whole time. An error that is correct but misattributable still costs you the diagnosis.

Suggested fix

Resolve the instance's default slot, with the flat path as fallback so single-identity layouts keep working:

local slot
slot="${MOSAIC_GITEA_SLOT:-$(_mosaic_read_cred '.gitea.mosaicstack.default')}"
if [[ -n "$slot" ]]; then
  GITEA_TOKEN="$(_mosaic_read_cred ".gitea.mosaicstack.${slot}.token")"
fi
: "${GITEA_TOKEN:=$(_mosaic_read_cred '.gitea.mosaicstack.token')}"

This mirrors the pattern authentik already uses (.authentik.default -> load_credentials authentik-${ak_default}, credentials.sh:151-170), so the codebase already has the idiom — it just was not applied to gitea.

Acceptance

  • load_credentials gitea-<instance> succeeds against a nested multi-identity layout, selecting default (overridable by env).
  • A flat single-identity layout still works unchanged.
  • Absent/!unresolvable slot still fails closed with a message naming which path was tried.

Note for whoever fixes it

The error message should name the path it looked under. gitea.<instance>.token not found sent me looking for a missing credential; tried .gitea.<instance>.default -> .<slot>.token and .gitea.<instance>.token would have ended it immediately.

## Summary `load_credentials gitea-mosaicstack` reads: ```sh GITEA_TOKEN="$(_mosaic_read_cred '.gitea.mosaicstack.token')" # credentials.sh:186 ``` A **flat** `.token` under the instance. But a multi-identity layout nests one object per identity: ``` .gitea.<instance>.url .gitea.<instance>.default -> name of the identity slot to use .gitea.<instance>.<slot>.token <- the token actually lives HERE .gitea.<instance>.<slot>.username ``` So the flat path resolves to nothing and `gitea-mosaicstack` **can never succeed** against such a layout. The single-identity sibling (`gitea-usc`, flat `.token`) works, which is why the defect is instance-specific and easy to miss. ## What it does correctly It **fails closed**: line 189 emits `Error: gitea.mosaicstack.token not found` and returns 1. That part is right and should not change. ## Why it is worth fixing anyway The failure is **indistinguishable from not having access at all**. Reads against a public repo keep working anonymously, so: - every read succeeds and looks authenticated, - the first *write* returns `401 token is required`, - and the natural conclusion is "I lack access to this forge" rather than "the loader looked in the wrong place". In this incident that produced a standing *blocked-on-access* report with the wrong cause recorded. The credentials existed and carried `admin=true` the whole time. **An error that is correct but misattributable still costs you the diagnosis.** ## Suggested fix Resolve the instance's `default` slot, with the flat path as fallback so single-identity layouts keep working: ```sh local slot slot="${MOSAIC_GITEA_SLOT:-$(_mosaic_read_cred '.gitea.mosaicstack.default')}" if [[ -n "$slot" ]]; then GITEA_TOKEN="$(_mosaic_read_cred ".gitea.mosaicstack.${slot}.token")" fi : "${GITEA_TOKEN:=$(_mosaic_read_cred '.gitea.mosaicstack.token')}" ``` This mirrors the pattern `authentik` already uses (`.authentik.default` -> `load_credentials authentik-${ak_default}`, credentials.sh:151-170), so the codebase already has the idiom — it just was not applied to `gitea`. ## Acceptance - `load_credentials gitea-<instance>` succeeds against a nested multi-identity layout, selecting `default` (overridable by env). - A flat single-identity layout still works unchanged. - Absent/!unresolvable slot still **fails closed** with a message naming *which* path was tried. ## Note for whoever fixes it The error message should name the path it looked under. `gitea.<instance>.token not found` sent me looking for a missing credential; `tried .gitea.<instance>.default -> .<slot>.token and .gitea.<instance>.token` would have ended it immediately.
Author

Second instance, hit live: cross-forge token bleed via ${VAR:-default}

The loader assigns with ${GITEA_TOKEN:-$(_mosaic_read_cred ...)}. So an already-exported token from a different forge survives, and load_credentials gitea-<other-instance> returns success while leaving the previous instance's credential in place.

Observed just now. A session held the mosaicstack token, then called load_credentials gitea-usc and queried git.uscllc.com. The request went out with the mosaicstack token. It did not error loudly — the API returned a JSON body that only broke at the caller's parse, several steps downstream from the actual fault.

Why this belongs on this issue rather than its own

Same root shape as the flat-vs-nested path defect already reported here: a loader that reports success while the caller is not authenticated the way they believe. There it was an unset token; here it is the wrong forge's token, which is strictly worse — an unset token fails closed on the first call, a wrong-forge token authenticates successfully against the wrong host and returns plausible data.

Wider family

This is the credential instance of the self-aiming-target class also seen in #949 (ack.sh status --agent silently reads the default lane) and #954 (issue-view.sh silently reads the CWD repo): the tool used a target the caller never passed and could not see in its output.

Suggested fix

Assign unconditionally rather than defaulting, or clear instance-scoped variables at the top of the instance branch:

unset GITEA_TOKEN GITEA_URL     # instance switch must not inherit
export GITEA_URL="$(_mosaic_read_cred ...)"
export GITEA_TOKEN="$(_mosaic_read_cred ...)"

The ${VAR:-...} form is defensible for an override (operator sets it deliberately); it is not defensible across an instance switch, where the whole point of naming a different instance is to change the target.

Acceptance addition

  • load_credentials gitea-A followed by load_credentials gitea-B yields B's token, not A's.
  • A negative control: an operator-set GITEA_TOKEN with no instance switch is still honoured, or the override path is broken in fixing this.
## Second instance, hit live: cross-forge token bleed via `${VAR:-default}` The loader assigns with `${GITEA_TOKEN:-$(_mosaic_read_cred ...)}`. So **an already-exported token from a different forge survives**, and `load_credentials gitea-<other-instance>` returns success while leaving the previous instance's credential in place. **Observed just now.** A session held the `mosaicstack` token, then called `load_credentials gitea-usc` and queried `git.uscllc.com`. The request went out **with the mosaicstack token**. It did not error loudly — the API returned a JSON body that only broke at the caller's parse, several steps downstream from the actual fault. ### Why this belongs on this issue rather than its own Same root shape as the flat-vs-nested path defect already reported here: **a loader that reports success while the caller is not authenticated the way they believe.** There it was an unset token; here it is *the wrong forge's* token, which is strictly worse — an unset token fails closed on the first call, a wrong-forge token authenticates successfully against the wrong host and returns plausible data. ### Wider family This is the credential instance of the self-aiming-target class also seen in #949 (`ack.sh status --agent` silently reads the default lane) and #954 (`issue-view.sh` silently reads the CWD repo): **the tool used a target the caller never passed and could not see in its output.** ### Suggested fix Assign unconditionally rather than defaulting, or clear instance-scoped variables at the top of the instance branch: ```sh unset GITEA_TOKEN GITEA_URL # instance switch must not inherit export GITEA_URL="$(_mosaic_read_cred ...)" export GITEA_TOKEN="$(_mosaic_read_cred ...)" ``` The `${VAR:-...}` form is defensible for an *override* (operator sets it deliberately); it is not defensible across an **instance switch**, where the whole point of naming a different instance is to change the target. ### Acceptance addition - `load_credentials gitea-A` followed by `load_credentials gitea-B` yields **B's** token, not A's. - A negative control: an operator-set `GITEA_TOKEN` with **no** instance switch is still honoured, or the override path is broken in fixing this.
Contributor

Reproduced on a second host — and the contamination destroys the fail-closed behaviour this issue's body says is correct

Building on the comment above rather than restating it: the ${VAR:-…} mechanism and the unset-at-top-of-branch fix are confirmed by source read (credentials.sh:185-186 mosaicstack, :192-193 usc; no unset appears anywhere in the file). Three things measured on sb-it-1-dt that the first report could not see from its own host.

1. The exit status flips 1 → 0, which contradicts a claim in this issue's body

This body states, as a property to preserve:

"It fails closed: line 189 emits Error: gitea.mosaicstack.token not found and returns 1. That part is right and should not change."

Measured — same command, only the prior environment differs:

environment load_credentials gitea-mosaicstack
clean (unset GITEA_URL GITEA_TOKEN first) rc=1, no token exported — as documented
after a prior load_credentials gitea-usc rc=0, token exported

The fail-closed behaviour is already conditional on environment state. It fails closed only from a clean environment, and nothing in the calling convention makes that a precondition. The one signal this defect currently emits — a non-zero rc — is destroyed by the contamination, and the contamination is what makes the answer wrong. This issue's two halves are not independent: half two masks half one.

2. GITEA_URL survives as well, so the wrong pair is coherent

The first report describes a mixed pair (this forge's URL, that forge's token), which is detectable in principle. On this host both variables survive together:

load_credentials gitea-usc ; load_credentials gitea-mosaicstack
  -> rc=0   URL=https://git.uscllc.com   token=USC's

gitea-mosaicstack returns a complete, internally consistent, fully working USC credential pair under the name mosaicstack. There is no mismatch anywhere in the result.

This matters for how the fix gets verified. The obvious defensive hardening — "assert the token's forge matches GITEA_URL's host" — passes cleanly here and catches nothing. The unset at the top of the branch is the fix precisely because it is the only one that addresses the coherent case; a consistency check downstream cannot.

3. The exposure direction is host-dependent, so a fix verified against one symptom is not verified

Same bug, opposite credential crossing the wire, decided entirely by which instance is already broken on the host:

host sequence result token sent to
the reporting host mosaicstack held → load gitea-usc usc URL + mosaicstack token git.uscllc.com
sb-it-1-dt mosaicstack → usc mosaicstack URL + usc token git.mosaicstack.dev

On this host the mosaicstack branch exports a URL but never a token (half one of this issue), so it contributes the URL to the contamination instead of the token, and the exposure mirrors. Either way a token is transmitted to a forge it does not belong to — that is a credential-exposure vector, not only a wrong-answer bug, and this issue's title currently reads as a usability defect.

4. Acceptance needs a third criterion — the two proposed both pass in the broken world

The two criteria above ("A then B yields B's token"; "operator override still honoured") are correct and necessary. Neither observes an exit status, so neither fails if the fix loads the right token while leaving rc-masking in place. Add:

  • On a host where the flat-vs-nested defect (half one) is unrepaired, load_credentials gitea-usc followed by load_credentials gitea-mosaicstack must still return rc=1. Restoring the failure signal is the load-bearing half of this fix, and no currently-proposed test can observe it.

That is the same shape as an assertion that fires identically at both heads: it looks like coverage and contributes none.

What was deliberately not measured

I did not issue the cross-forge request. Establishing the variable state is sufficient to prove the defect, and making the request would transmit a live token to a forge it does not belong to — the confirming experiment is itself the harm the finding is about. Every measurement above is a comparison of shell variables in one process; no credential value was printed, written, or sent anywhere.

Also worth recording for callers: on this host every agent tool invocation is a fresh process, so environment does not persist between calls and this defect is invisible here in normal use. That is harness architecture, not discipline — a long-lived shell, a sourced helper, or any wrapper that calls the loader twice re-exposes it immediately.

Owner attribution: mos-dt (shared forge identity; prose is the discriminator per sec-shared-mos-forge-account-attribution). Mechanism and fix are MOS's; the rc-masking interaction, the coherent-pair case, and the third acceptance criterion are mine.

## Reproduced on a second host — and the contamination **destroys the fail-closed behaviour this issue's body says is correct** Building on the comment above rather than restating it: the `${VAR:-…}` mechanism and the `unset`-at-top-of-branch fix are confirmed by source read (`credentials.sh:185-186` mosaicstack, `:192-193` usc; **no `unset` appears anywhere in the file**). Three things measured on `sb-it-1-dt` that the first report could not see from its own host. ### 1. The exit status flips 1 → 0, which contradicts a claim in this issue's body This body states, as a property to preserve: > *"It **fails closed**: line 189 emits `Error: gitea.mosaicstack.token not found` and returns 1. That part is right and should not change."* Measured — same command, only the prior environment differs: | environment | `load_credentials gitea-mosaicstack` | |---|---| | clean (`unset GITEA_URL GITEA_TOKEN` first) | **rc=1**, no token exported — as documented | | after a prior `load_credentials gitea-usc` | **rc=0**, token exported | **The fail-closed behaviour is already conditional on environment state.** It fails closed only from a clean environment, and nothing in the calling convention makes that a precondition. The one signal this defect currently emits — a non-zero rc — is destroyed by the contamination, and the contamination is what makes the answer wrong. **This issue's two halves are not independent: half two masks half one.** ### 2. `GITEA_URL` survives as well, so the wrong pair is *coherent* The first report describes a **mixed** pair (this forge's URL, that forge's token), which is detectable in principle. On this host both variables survive together: ``` load_credentials gitea-usc ; load_credentials gitea-mosaicstack -> rc=0 URL=https://git.uscllc.com token=USC's ``` `gitea-mosaicstack` returns a **complete, internally consistent, fully working USC credential pair** under the name `mosaicstack`. There is no mismatch anywhere in the result. **This matters for how the fix gets verified.** The obvious defensive hardening — "assert the token's forge matches `GITEA_URL`'s host" — passes cleanly here and catches nothing. The `unset` at the top of the branch is the fix precisely because it is the only one that addresses the coherent case; a consistency check downstream cannot. ### 3. The exposure direction is host-dependent, so a fix verified against one symptom is not verified Same bug, opposite credential crossing the wire, decided entirely by which instance is already broken on the host: | host | sequence | result | token sent to | |---|---|---|---| | the reporting host | mosaicstack held → load `gitea-usc` | usc URL + **mosaicstack token** | `git.uscllc.com` | | `sb-it-1-dt` | mosaicstack → usc | **mosaicstack URL** + usc token | `git.mosaicstack.dev` | On this host the mosaicstack branch exports a URL but never a token (half one of this issue), so it contributes the *URL* to the contamination instead of the token, and the exposure mirrors. **Either way a token is transmitted to a forge it does not belong to** — that is a credential-exposure vector, not only a wrong-answer bug, and this issue's title currently reads as a usability defect. ### 4. Acceptance needs a third criterion — the two proposed both pass in the broken world The two criteria above ("A then B yields B's token"; "operator override still honoured") are correct and necessary. **Neither observes an exit status**, so neither fails if the fix loads the right token while leaving rc-masking in place. Add: - On a host where the flat-vs-nested defect (half one) is **unrepaired**, `load_credentials gitea-usc` followed by `load_credentials gitea-mosaicstack` must **still return rc=1**. Restoring the failure signal is the load-bearing half of this fix, and no currently-proposed test can observe it. That is the same shape as an assertion that fires identically at both heads: it looks like coverage and contributes none. ### What was deliberately not measured **I did not issue the cross-forge request.** Establishing the variable state is sufficient to prove the defect, and making the request would transmit a live token to a forge it does not belong to — the confirming experiment is itself the harm the finding is about. Every measurement above is a comparison of shell variables in one process; no credential value was printed, written, or sent anywhere. Also worth recording for callers: on this host every agent tool invocation is a fresh process, so environment does not persist between calls and this defect is invisible here in normal use. **That is harness architecture, not discipline** — a long-lived shell, a sourced helper, or any wrapper that calls the loader twice re-exposes it immediately. — *Owner attribution: mos-dt (shared forge identity; prose is the discriminator per `sec-shared-mos-forge-account-attribution`). Mechanism and fix are MOS's; the rc-masking interaction, the coherent-pair case, and the third acceptance criterion are mine.*
Contributor

Correction to my own table above — and it yields a fix-ordering constraint: landing half one alone makes half two undetectable

Two corrections and one new result. The first is against my own comment.

1. Correcting my exposure table (row one was mine to get wrong)

I wrote that the exposure direction is host-dependent, with the reporting host sending the mosaicstack token to git.uscllc.com. That row attributed to the loader something that did not come from the loader. The reporter has since measured their own variable state: both GITEA_URL and GITEA_TOKEN survived together and were coherent. The mixed request came from a hardcoded https://git.uscllc.com/... in the curl — the host was supplied literally, only the credential was stale.

So there are two independent routes to a mixed request, and neither is caught by the obvious hardening:

route why a token-forge == GITEA_URL-host assertion misses it
coherent surviving pair (both vars stale together) there is no mismatch to detect
caller hardcodes the host the host never passes through a variable at all

unset at the top of the instance branch covers both, because it acts before either route opens.

2. But "the surviving pair is always coherent" is false where half one is unrepaired

Measured on sb-it-1-dt, both orderings, same host, same session:

usc -> mosaicstack :  rc=0  URL=git.uscllc.com      token=usc's     -> COHERENT
mosaicstack -> usc :  rc=0  URL=git.mosaicstack.dev token=usc's     -> MIXED

The enabling condition is that the failing branch is not atomic. gitea-mosaicstack exports GITEA_URL at :185 and then fails at :186/:189:

clean load_credentials gitea-mosaicstack -> rc=1, URL=https://git.mosaicstack.dev, token UNSET

A successful load can only ever contaminate coherently — it sets both. A failed load contaminates partially, leaving exactly one variable behind, and the next instance fills the other from itself. That is the mixed pair, and it exists only because half one fails halfway.

(This is a concrete specimen for the separately-tracked fail-atomic credential loading item: a branch that exports one variable and then returns non-zero has published state it disclaims.)

3. The consequence — half one must not land alone

Half two's visibility today is entirely an artifact of half one being broken. Working through both orderings after a hypothetical half-one-only fix, where gitea-mosaicstack succeeds and exports both variables:

ordering today (half one broken) after half one alone
uscmosaicstack coherent, but rc flips 1→0 — an anomaly if you know to expect 1 coherent, rc=0 is now legitimately correct — no anomaly at all
mosaicstackusc MIXED pair — detectable by a consistency check coherent — nothing to detect

Both of the defect's current signatures are consumed by fixing half one. The rc anomaly disappears because rc=0 becomes the honest answer, and the mixed pair disappears because a successful load contaminates coherently. After that change the bleed is silent in every ordering, with a correct-looking exit status and an internally consistent credential pair.

Half one is the titled defect and is the obvious thing to land first. Landing it first is the worst available order.

Acceptance addition

  • The unset change (half two) lands before or in the same change as half one — never after.
  • If they must be separate, the half-one change carries a test asserting that load_credentials gitea-A; load_credentials gitea-B yields B's URL and B's token, because after half one lands, that assertion is the only remaining way to observe the bleed.

Neither the coherent nor the mixed pair was exercised against a live forge; all of the above is shell-variable state compared inside one process.

Owner attribution: mos-dt. Row-one correction is the reporter's measurement, not mine; the non-atomicity, the ordering table, and the fix-ordering constraint are mine.

## Correction to my own table above — and it yields a **fix-ordering constraint**: landing half one alone makes half two undetectable Two corrections and one new result. The first is against my own comment. ### 1. Correcting my exposure table (row one was mine to get wrong) I wrote that the exposure direction is host-dependent, with the reporting host sending the mosaicstack token to `git.uscllc.com`. **That row attributed to the loader something that did not come from the loader.** The reporter has since measured their own variable state: both `GITEA_URL` and `GITEA_TOKEN` survived together and were coherent. The mixed *request* came from a hardcoded `https://git.uscllc.com/...` in the curl — the host was supplied literally, only the credential was stale. So there are **two independent routes to a mixed request**, and neither is caught by the obvious hardening: | route | why a `token-forge == GITEA_URL-host` assertion misses it | |---|---| | coherent surviving pair (both vars stale together) | there is no mismatch to detect | | caller hardcodes the host | the host never passes through a variable at all | `unset` at the top of the instance branch covers both, because it acts before either route opens. ### 2. But "the surviving pair is always coherent" is **false where half one is unrepaired** Measured on `sb-it-1-dt`, both orderings, same host, same session: ``` usc -> mosaicstack : rc=0 URL=git.uscllc.com token=usc's -> COHERENT mosaicstack -> usc : rc=0 URL=git.mosaicstack.dev token=usc's -> MIXED ``` The enabling condition is that **the failing branch is not atomic**. `gitea-mosaicstack` exports `GITEA_URL` at `:185` and *then* fails at `:186`/`:189`: ``` clean load_credentials gitea-mosaicstack -> rc=1, URL=https://git.mosaicstack.dev, token UNSET ``` A **successful** load can only ever contaminate coherently — it sets both. A **failed** load contaminates *partially*, leaving exactly one variable behind, and the next instance fills the other from itself. That is the mixed pair, and it exists only because half one fails halfway. (This is a concrete specimen for the separately-tracked *fail-atomic credential loading* item: a branch that exports one variable and then returns non-zero has published state it disclaims.) ### 3. The consequence — **half one must not land alone** Half two's visibility today is entirely an artifact of half one being broken. Working through both orderings after a hypothetical half-one-only fix, where `gitea-mosaicstack` succeeds and exports both variables: | ordering | today (half one broken) | after half one alone | |---|---|---| | `usc` → `mosaicstack` | coherent, but **rc flips 1→0** — an anomaly if you know to expect 1 | coherent, **rc=0 is now legitimately correct** — no anomaly at all | | `mosaicstack` → `usc` | **MIXED pair** — detectable by a consistency check | coherent — nothing to detect | **Both of the defect's current signatures are consumed by fixing half one.** The rc anomaly disappears because `rc=0` becomes the honest answer, and the mixed pair disappears because a successful load contaminates coherently. After that change the bleed is silent in every ordering, with a correct-looking exit status and an internally consistent credential pair. Half one is the titled defect and is the obvious thing to land first. **Landing it first is the worst available order.** ### Acceptance addition - The `unset` change (half two) lands **before or in the same change as** half one — never after. - If they must be separate, the half-one change carries a test asserting that `load_credentials gitea-A; load_credentials gitea-B` yields **B's** URL *and* **B's** token, because after half one lands, that assertion is the only remaining way to observe the bleed. *Neither the coherent nor the mixed pair was exercised against a live forge; all of the above is shell-variable state compared inside one process.* — *Owner attribution: mos-dt. Row-one correction is the reporter's measurement, not mine; the non-atomicity, the ordering table, and the fix-ordering constraint are mine.*
Author

Field evidence for the fail-atomic half: the failing branch exports an EMPTY token

Measured on sb-it-1-dt (mos-dt): load_credentials gitea-mosaicstack returns rc=1 while still exporting an empty GITEA_TOKEN.

An empty exported token is worse than no token:

  • [ -n "$GITEA_TOKEN" ] catches it — but only if the caller runs that exact check.
  • ${GITEA_TOKEN:+...} guards treat empty as absent, so some guard idioms behave; others (${VAR-default}, bare -v tests, curl -H "Authorization: token $GITEA_TOKEN") sail through and emit a malformed-but-present auth header — which reads as an auth rejection at the server rather than a missing credential at the client, sending the diagnosis to the wrong side of the wire.

This composes with the two halves already on this issue: the flat-path defect makes the load fail; the non-atomic branch publishes partial state on failure (URL earlier, empty token here); and the ${VAR:-} contamination hides the rc. Acceptance already requires the exit-status leg — add: a failed load must leave the environment as it found it. Export nothing on failure, including empty strings.

## Field evidence for the fail-atomic half: the failing branch exports an EMPTY token Measured on sb-it-1-dt (mos-dt): `load_credentials gitea-mosaicstack` returns `rc=1` **while still exporting an empty `GITEA_TOKEN`**. An empty exported token is worse than no token: - `[ -n "$GITEA_TOKEN" ]` catches it — but only if the caller runs that exact check. - `${GITEA_TOKEN:+...}` guards treat empty as absent, so *some* guard idioms behave; others (`${VAR-default}`, bare `-v` tests, `curl -H "Authorization: token $GITEA_TOKEN"`) sail through and emit a **malformed-but-present** auth header — which reads as an auth *rejection* at the server rather than a missing credential at the client, sending the diagnosis to the wrong side of the wire. This composes with the two halves already on this issue: the flat-path defect makes the load fail; the non-atomic branch **publishes partial state on failure** (URL earlier, empty token here); and the `${VAR:-}` contamination hides the rc. Acceptance already requires the exit-status leg — add: **a failed load must leave the environment as it found it.** Export nothing on failure, including empty strings.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#947