Chapter 13Lesson 05230–310 min

Checkpoint Lab — Proxy Caching, Negative Cache, Remote Storage, Routing Rules, Repository Health, and Failure Behavior

Execute a controlled hit/miss/negative-cache/routing/upstream-failure experiment, predict state transitions, collect an evidence packet, apply least-destructive remediation, and clean up only the disposable repository state.

Checkpoint labHit/missNegative cacheFailure drillEvidence

Checkpoint objectives

  • Build a disposable public-read proxy topology and record exact starting state.
  • Predict cache/blob/metadata/negative-cache/routing changes before triggering them.
  • Execute hit, miss, negative-cache, routing-rule and upstream-blocked phases one variable at a time.
  • Collect request, component/asset, checksum, configuration and failure evidence without secrets.
  • Apply least-destructive remediation and remove only the lab repositories and rules.

Checkpoint scope. Use a disposable Nexus Community instance and synthetic repository/rule names. The current reference baseline is the 3.95.2 verified image line, but your evidence packet must record the actual running version. The upstream is public Maven Central and is never modified. Outage simulation uses Nexus Blocked state, not DNS/firewall sabotage. Do not run this checkpoint against a shared organization proxy.

1. Scenario and required prediction

You are validating whether a proposed proxy policy can keep already-used dependencies available during an upstream incident, suppress repeated misses, and stop an internal namespace from leaking to a public repository.

Before touching Nexus, write predictions for at least these four transitions:

Phase Prediction to write first
Positive miss → fill Which repository gains asset/blob metadata after first JUnit request?
Repeat read Will the returned bytes change? Is a remote check required under the chosen age?
Negative miss What state exists even though no positive asset is stored?
Blocked outage Which already-cached request still works and which previously uncached request cannot be retrieved?
Routing rule Which namespace is prevented from reaching the public remote?

2. Preflight and evidence workspace

export NX_URL="http://127.0.0.1:8081"
export LAB="${TMPDIR:-/tmp}/nexus-ch13-checkpoint"
rm -rf "$LAB"
mkdir -p "$LAB"/{evidence,downloads}
chmod 700 "$LAB"

curl -fsS "$NX_URL/service/rest/v1/status"   | tee "$LAB/evidence/status.txt"

read -r -p 'Disposable Nexus username: ' NX_USER
read -r -s -p 'Disposable Nexus password: ' NX_PASS; echo
export NX_AUTH_FILE="$LAB/nexus.netrc"
umask 077
printf 'machine 127.0.0.1 login %s password %s
' "$NX_USER" "$NX_PASS" > "$NX_AUTH_FILE"
unset NX_PASS

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/repositories"   > "$LAB/evidence/repositories-before.json"

Windows/PowerShell: use a protected temporary directory and curl.exe. PowerShell's Get-FileHash -Algorithm SHA256 is the equivalent checksum command. Keep credentials out of command arguments and remove the temporary secret file at the end.

3. Create the two-repository topology

Create through Settings → Repository → Repositories:

Repository Recipe Configuration
academy-ch13cp-central maven2 (proxy) Remote https://repo1.maven.org/maven2/; Release; Strict; component age 60; metadata age 5; negative cache enabled TTL 5; Auto Blocking enabled.
academy-ch13cp-public maven2 (group) Single member: academy-ch13cp-central.

Record screenshots or a text runbook of those exact fields. Do not reuse maven-central because the checkpoint needs empty initial cache state.

4. Phase A — positive miss and cache fill

export GROUP_URL="$NX_URL/repository/academy-ch13cp-public"
export JUNIT_PATH="junit/junit/4.13.2/junit-4.13.2.pom"

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/assets?repository=academy-ch13cp-central"   > "$LAB/evidence/assets-phase-a-before.json"

curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/downloads/junit-a.pom"   -w 'phase-a http=%{http_code} total=%{time_total} size=%{size_download}
'   "$GROUP_URL/$JUNIT_PATH"   | tee "$LAB/evidence/phase-a-request.txt"

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/assets?repository=academy-ch13cp-central"   > "$LAB/evidence/assets-phase-a-after.json"
grep -F 'junit-4.13.2.pom' "$LAB/evidence/assets-phase-a-after.json"
sha256sum "$LAB/downloads/junit-a.pom" > "$LAB/evidence/junit-a.sha256"

Expected causal result: the proxy gains the JUnit asset/component metadata and blob-backed content after the first successful request.

5. Phase B — repeat read and byte identity

curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/downloads/junit-b.pom"   -w 'phase-b http=%{http_code} total=%{time_total} size=%{size_download}
'   "$GROUP_URL/$JUNIT_PATH"   | tee "$LAB/evidence/phase-b-request.txt"

sha256sum "$LAB/downloads/junit-b.pom" > "$LAB/evidence/junit-b.sha256"
cmp -s "$LAB/downloads/junit-a.pom" "$LAB/downloads/junit-b.pom"   && echo 'same bytes: PASS' | tee "$LAB/evidence/phase-b-identity.txt"

Do not claim that checksum equality proves trusted provenance. It proves the two observed downloads are byte-identical. Source trust, signatures, provenance and vulnerability policy are separate controls.

6. Phase C — negative cache

export MISS_PATH="dev/academy/ch13/nonexistent/never-publish/0.0.0/never-publish-0.0.0.pom"
for n in 1 2; do
  curl -sS --netrc-file "$NX_AUTH_FILE"     -o "$LAB/evidence/negative-$n-body.txt"     -w "negative-$n http=%{http_code} total=%{time_total}
"     "$GROUP_URL/$MISS_PATH"     | tee "$LAB/evidence/negative-$n-metrics.txt"
done

Verify the path is absent from the Assets API. Record the configured five-minute negative TTL. If temporary request logging is enabled on this disposable instance, add only the relevant redacted lines to the evidence packet; otherwise treat timing as supportive, not definitive, evidence of the negative-cache hit.

7. Phase D — routing policy experiment

Create a BLOCK routing rule academy-ch13cp-no-internal-public with matcher .*dev/academy/internal/.*. In the built-in tester, confirm a path under that namespace is blocked and junit/junit/4.13.2/... is allowed. Assign the rule to academy-ch13cp-central.

Request a synthetic internal path through the group and capture the response. The expected invariant is that Nexus does not forward that matching path to Maven Central. Then request cached JUnit again to prove the targeted routing rule did not destroy unrelated cached content.

8. Phase E — safe upstream-failure injection

Edit academy-ch13cp-central and check Blocked. Capture that configuration state. Then run:

# Cached content.
curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/downloads/junit-blocked.pom"   -w 'blocked-cached http=%{http_code} total=%{time_total}
'   "$GROUP_URL/$JUNIT_PATH"   | tee "$LAB/evidence/blocked-cached.txt"

# Known Central release intentionally not requested earlier.
export NEW_PATH="commons-codec/commons-codec/1.17.0/commons-codec-1.17.0.pom"
curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/evidence/blocked-new-body.txt"   -w 'blocked-new http=%{http_code} total=%{time_total}
'   "$GROUP_URL/$NEW_PATH"   | tee "$LAB/evidence/blocked-new.txt"

Record the actual HTTP result from the uncached request rather than forcing a specific status-code expectation. The important observation is that Nexus serves cached JUnit locally but does not retrieve the new Commons Codec POM while manually blocked.

9. Least-destructive remediation

  1. Clear Blocked on the disposable proxy.
  2. Repeat only $NEW_PATH and verify it can now populate the proxy.
  3. Do not invalidate all caches merely because the remote was blocked.
  4. If you want to prove the negative-cache control separately, use Invalidate Cache on this disposable proxy and repeat only $MISS_PATH; it should still be not-found because the upstream truly lacks it.
  5. Keep the routing rule in place until the namespace-governance evidence is complete.

10. Optional Community RHC observation

If outbound access to Sonatype RHC services is permitted in the lab environment, enable Repository Health Check on the disposable Maven proxy and record only the summary state. It may take time to populate and is not required for cache correctness. Do not represent the Pro detailed report as Community functionality.

11. Required evidence packet

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/repositories"   > "$LAB/evidence/repositories-after.json"
curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/assets?repository=academy-ch13cp-central"   > "$LAB/evidence/assets-final.json"

python - <<'PYI'
from pathlib import Path
import os, json
lab=Path(os.environ['LAB'])
summary={
  'proxy':'academy-ch13cp-central',
  'group':'academy-ch13cp-public',
  'remote':'https://repo1.maven.org/maven2/',
  'component_age_minutes':60,
  'metadata_age_minutes':5,
  'negative_cache_ttl_minutes':5,
  'routing_rule':'academy-ch13cp-no-internal-public',
  'outage_simulation':'Nexus manual Blocked flag only',
  'direct_blob_or_database_edits':False,
  'credentials_in_evidence':False,
}
(lab/'evidence/checkpoint-summary.json').write_text(json.dumps(summary,indent=2)+'
')
PYI

find "$LAB/evidence" -maxdepth 1 -type f -printf '%f
' 2>/dev/null   | sort > "$LAB/evidence/files.txt" || true
cat "$LAB/evidence/files.txt"

Review every evidence file before sharing it. The netrc file is intentionally outside the evidence directory and must never be included in a support bundle.

12. Verification checklist

  • Actual Nexus version/edition and repository list were captured before changes.
  • First successful JUnit request created observable proxy asset state.
  • Second JUnit response is byte-identical to the first.
  • Negative request produced no positive asset and the TTL was recorded.
  • Routing rule was tested before assignment and only targets the intended namespace.
  • Manual Blocked mode preserved cached JUnit while preventing retrieval of a previously uncached known artifact.
  • Unblocking restored remote retrieval without deleting caches.
  • No credential appears in command arguments, evidence JSON, screenshots or URLs.
  • No database row, blob file, DNS entry, firewall rule or public upstream was modified.

13. Cleanup and rollback

  1. Clear Blocked if still set.
  2. Unassign academy-ch13cp-no-internal-public from the proxy.
  3. Delete the group academy-ch13cp-public.
  4. Delete the proxy academy-ch13cp-central.
  5. Delete the now-unassigned routing rule.
  6. Confirm all three names are absent from Nexus before removing local lab state.
rm -f "$NX_AUTH_FILE"
unset NX_AUTH_FILE NX_USER GROUP_URL JUNIT_PATH MISS_PATH NEW_PATH
# Archive reviewed evidence elsewhere if desired, then remove the disposable workspace.
rm -rf "$LAB"

Deleting the repository through supported Nexus operations is the cleanup boundary. Never remove its blob-store directory directly.

14. Knowledge check

During Blocked mode, JUnit still downloads but a never-before-requested package does not. What does this prove?

Why is the fake negative-cache coordinate intentionally never published?

A routing rule blocks the internal namespace. What should you change if that matcher is wrong?

Why does the evidence packet record cache ages explicitly?

What is the production lesson from the outage phase?

15. Production operating-model addition and bridge to Chapter 14

This chapter adds an explicit proxy runbook to the artifact platform: freshness SLOs, negative-cache policy, egress routing, upstream dependency evidence, auto-blocking behavior, scoped remediation and outage drills. Operators can now explain why a dependency was or was not fetched instead of treating the proxy as an opaque mirror.

Chapter 14 moves from remote egress control to who may see which repository paths: content selectors, repository targets, fine-grained privileges and path-level authorization.

Official references and version notes

Version-sensitive statements were rechecked on 2026-08-26. Sonatype's verified container registry exposes Nexus Repository 3.95.2 as the current latest image line, while the archive-download documentation may lag at 3.94.1. This chapter therefore records the actual running server version as authoritative evidence and uses 3.95.2 only as the concrete current reference baseline. The mandatory work requires Community-compatible proxy repositories, routing rules, cache controls and the Repository Health Check summary only; no Pro-only detailed RHC report, staging, HA, user tokens, export/import or Repository Firewall capability is required.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.