Chapter 03Lesson 02~145 minutes

User Interface, Search, Browse, Components, Assets, Tags, Uploads, and Repository Navigation: Guided Hands-On Workflow and Core Operations

Use the disposable Community Edition instance from Chapter 02 to inventory repositories, create one synthetic hosted Maven repository, upload a harmless component with multiple assets, and prove the resulting state independently through Browse, Search, REST, and direct retrieval.

Disposable hosted repoComponents APISearch APIDirect retrievalEvidence-driven lab

Learning objectives

  • Inventory the current repository list and distinguish format/type before creating any disposable state.
  • Create one hosted Maven repository with write-once behavior and explain each configuration field rather than treating JSON as a recipe.
  • Upload one synthetic Maven component containing two assets through the Components API and predict database/blob changes first.
  • Verify the same artifact through Search, Assets API and direct repository retrieval, including checksum comparison.
  • Cleanly separate mandatory CE actions from optional Pro-only tagging.

Disposable instance only. This lesson assumes the Chapter 02 single-node lab remains on 127.0.0.1:8081. Do not run repository-creation or upload commands against an employer instance. The Maven format is used only because its group/name/version model makes component-versus-assets visible; Chapter 06 teaches Maven repository administration in depth.

1. Preflight and evidence boundary

Before creating anything, confirm readiness, capture repository inventory and create the temporary credential file. If the instance, version or repository list differs from Chapter 02, record that discrepancy rather than silently adapting production-like commands.

LAB="$HOME/nexus-ch03-lab"
NX_URL="http://127.0.0.1:8081"
mkdir -p "$LAB/evidence" "$LAB/payload"
umask 077
read -rsp "Disposable Nexus admin password: " NX_PASS; printf "\n"
printf 'machine 127.0.0.1 login admin password %s\n' "$NX_PASS" > "$LAB/nexus.netrc"
unset NX_PASS
NETRC="$LAB/nexus.netrc"

# Never commit or print $NETRC. Delete it when the lab ends.
curl -fsS "$NX_URL/service/rest/v1/status" | tee "$LAB/evidence/01-status.txt"
curl --fail-with-body --silent --show-error --netrc-file "$NETRC" \
  "$NX_URL/service/rest/v1/repositories" \
  | tee "$LAB/evidence/02-repositories-before.json"

# Optional readable formatting without jq:
python3 -m json.tool "$LAB/evidence/02-repositories-before.json" \
  > "$LAB/evidence/03-repositories-before.pretty.json"

In the UI, open Browse and inspect whatever repositories the current install already has. Do not assume a tutorial-era default list is universal. Record each observed repository's name, format and type; those three fields determine which operations make sense.

2. Predict the mutation before creating it

Prediction Expected state change Independent proof
P-01 Create hosted repository A repository configuration record named academy-ch03-hosted; no learner component yet. Repositories API + Browse repository list.
P-02 Upload component One component record for com.example.academy:ui-demo:1.0.0 plus JAR/POM assets and blob bytes. Search API + Assets API + direct GET + checksum.
P-03 Repeat same release upload With ALLOW_ONCE, a conflicting redeploy should be rejected rather than silently replacing release bytes. Preserved HTTP response and unchanged downloaded SHA-256.

3. Create one disposable hosted repository

The Repositories API is used because it gives a reproducible payload and an observable HTTP result. hosted means this repository is an organization-controlled publication target. ALLOW_ONCE is intentionally chosen to make the lab's release coordinate immutable after first deployment.

cat > "$LAB/maven-hosted.json" <<'JSON'
{
  "name": "academy-ch03-hosted",
  "online": true,
  "storage": {
    "blobStoreName": "default",
    "strictContentTypeValidation": true,
    "writePolicy": "ALLOW_ONCE"
  },
  "maven": {
    "versionPolicy": "RELEASE",
    "layoutPolicy": "STRICT",
    "contentDisposition": "INLINE"
  }
}
JSON

curl --fail-with-body --silent --show-error --netrc-file "$NETRC" \
  -H 'Content-Type: application/json' \
  -X POST "$NX_URL/service/rest/v1/repositories/maven/hosted" \
  --data-binary @"$LAB/maven-hosted.json"
curl --fail-with-body --silent --show-error --netrc-file "$NETRC" \
  "$NX_URL/service/rest/v1/repositories/maven/hosted/academy-ch03-hosted" \
  | tee "$LAB/evidence/04-hosted-config.json"

No artifact bytes should exist because repository creation changes configuration, not package content. If Browse immediately shows learner files, stop and investigate whether you reused a non-disposable name or an old data directory.

4. Build harmless local payloads and record identity

The JAR contains only a text file and the POM uses the reserved example namespace. The SHA-256 values recorded before upload become the independent byte identities used after retrieval.

python3 - <<'PY'
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
root = Path.home() / "nexus-ch03-lab" / "payload"
root.mkdir(parents=True, exist_ok=True)
(root / "README.txt").write_text("synthetic Chapter 03 artifact\n", encoding="utf-8")
with ZipFile(root / "ui-demo-1.0.0.jar", "w", ZIP_DEFLATED) as z:
    z.write(root / "README.txt", "README.txt")
(root / "ui-demo-1.0.0.pom").write_text("""<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example.academy</groupId>
  <artifactId>ui-demo</artifactId>
  <version>1.0.0</version>
</project>
""", encoding="utf-8")
PY
sha256sum "$LAB/payload/ui-demo-1.0.0.jar" "$LAB/payload/ui-demo-1.0.0.pom" \
  | tee "$LAB/evidence/05-local-sha256.txt"

Creating the local files changes only the learner workstation. Nexus still knows nothing about the component at this point.

5. Upload one component with two assets

The Components API accepts format-specific multipart fields. For Maven, group ID, artifact ID and version identify the component, while each assetN field identifies a file attached to that component. A successful upload currently returns HTTP 204 with no response body.

curl --fail-with-body --silent --show-error --netrc-file "$NETRC" \
  -X POST "$NX_URL/service/rest/v1/components?repository=academy-ch03-hosted" \
  -F maven2.groupId=com.example.academy \
  -F maven2.artifactId=ui-demo \
  -F maven2.version=1.0.0 \
  -F maven2.asset1=@"$LAB/payload/ui-demo-1.0.0.jar" \
  -F maven2.asset1.extension=jar \
  -F maven2.asset2=@"$LAB/payload/ui-demo-1.0.0.pom" \
  -F maven2.asset2.extension=pom

After this request, the database should contain component/asset metadata and the default blob store should contain the uploaded bytes. Do not search the blob-store filesystem for filenames; Nexus blob internals are not the operator API.

6. Verify through three independent views

Q='repository=academy-ch03-hosted&group=com.example.academy&name=ui-demo&version=1.0.0'
curl --fail-with-body --silent --show-error --netrc-file "$NETRC" \
  "$NX_URL/service/rest/v1/search?$Q" \
  | tee "$LAB/evidence/07-search.json"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC" \
  "$NX_URL/service/rest/v1/assets?repository=academy-ch03-hosted" \
  | tee "$LAB/evidence/08-assets.json"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC" \
  "$NX_URL/repository/academy-ch03-hosted/com/example/academy/ui-demo/1.0.0/ui-demo-1.0.0.jar" \
  -o "$LAB/evidence/downloaded-ui-demo-1.0.0.jar"
sha256sum "$LAB/evidence/downloaded-ui-demo-1.0.0.jar" \
  | tee "$LAB/evidence/09-downloaded-sha256.txt"

Now use the browser: Browse → academy-ch03-hosted → navigate the Maven path and select the component/assets. Then use Search for group com.example.academy, name ui-demo, version 1.0.0. Compare the UI labels with the REST JSON and direct URL. The views should describe the same component identity, but each exposes different details.

The asset response may include multiple checksum algorithms. The local SHA-256 comparison proves whether the bytes you downloaded match the bytes created for the lab; it does not prove origin trust or vulnerability safety.

7. Tags: learn the model without making Pro mandatory

Current Sonatype documentation marks component tagging as Nexus Repository Pro. Therefore the required CE path records a small external evidence manifest instead of pretending the product feature is available.

cat > "$LAB/evidence/10-tag-simulation.json" <<'JSON'
{
  "simulation": true,
  "reason": "Nexus Repository component tagging is Pro-only in the current feature matrix",
  "logicalTag": "chapter03-build-001",
  "component": "com.example.academy:ui-demo:1.0.0",
  "scope": "component, not individual asset"
}
JSON

If you intentionally use a disposable Pro trial, you may create/associate a real tag through the documented Tags API, but record the license prerequisite and never make that result a checkpoint pass condition.

8. Challenge: choose the correct control

You need a second file attached to the already published 1.0.0 release. Should you retry the same generic upload, change the hosted repository write policy, use a native Maven publication, or publish a new version? Because this lab deliberately uses ALLOW_ONCE, do not weaken the repository to make a release mutable. Treat the artifact as immutable and publish 1.0.1 for a changed release payload. Chapter 06 will examine Maven publication semantics in depth.

Knowledge check

Why did the lesson create a Maven hosted repository instead of uploading to a proxy repository?

The Components API returned HTTP 204. Where should you look for proof that two assets actually exist?

The downloaded JAR SHA-256 matches the local JAR. What has been proven?

Why is the tag-simulation file acceptable in Community Edition?

A repeat upload of 1.0.0 fails under ALLOW_ONCE. Should you switch to an allow-redeploy policy?

9. Summary

You created configuration, then content, and proved the causal difference. Repository creation produced no learner assets; component upload produced one logical component with multiple assets and blob content; Search, Assets and direct retrieval independently exposed the result.

Next lesson

Configuration, design choices, and tradeoffs

Next you will decide when to prefer native package publication versus generic upload, browser workflows versus REST, Browse versus Search, and operator convenience versus least privilege.

Official references and version notes

  • Download Nexus Repository — the official download page used to pin the same 3.94.1-06 self-hosted lab baseline as Chapter 02 while current indexes are rechecked before execution.
  • Browsing Repositories — browse-tree behavior, 10,000-component-per-level UI limit, HTML view, and browse/read privilege distinctions.
  • Searching for Components — current UI search behavior, first-300-result display, SQL-search semantics, tokenization, exact phrases, and wildcard rules.
  • Search API — component/asset search endpoints and continuation-token pagination.
  • Viewing Component Information — component identifiers and the relationship to associated assets.
  • Viewing Asset Information — asset path, content type, size, blob timestamps/reference, checksums, uploader metadata, and format-specific attributes.
  • Uploading Components — hosted-only UI upload boundary and required upload/browse/read privileges.
  • Components API — list/get/delete components and format-specific multipart component upload.
  • Assets API — paginated asset listing, asset details, paths, download URLs and checksums.
  • Repositories API — repository inventory and format/type-specific repository configuration endpoints.
  • Privileges — current browse, read, add, edit, delete and search privilege semantics.
  • Tagging and Self-Hosted Feature Matrix — component tagging is currently a Pro feature; mandatory Chapter 03 work does not require it.

Version-sensitive statements were rechecked against Sonatype primary documentation on 2026-08-26. The mandatory path remains self-hosted, Community/free-compatible and disposable. The chapter keeps the Chapter 02 lab baseline at Nexus Repository 3.94.1-06 because Sonatype's current download and versions-status pages still present 3.94.1 as the current downloadable/GA line; learners are told to re-check those pages before running the lab.

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.