English | 中文
A CLI for governing Nexus Repository 3.76 guest / anonymous access.
The first version solves one problem: a guest (anonymous user) can see too
many repositories and artifacts in the Nexus UI. Nexus does not support
"grant browse to all repositories except one", so nexus-cli reads the
repository list, builds per-repository repository-view privileges, and binds
them to a guest role — granting browse+read to public repos and no guest
access at all to protected repos that must stay invisible and non-downloadable.
The older readOnly policy remains available for the advanced case where a
repo should be hidden from UI browse but still downloadable by exact URL.
See doc/nexus-cli第一版本PRD.md for the full product spec.
A second use case is per-user path-scoped read-only access: user create-readonly creates a
content selector, a path-scoped browse+read privilege, a role, and a user so
a named person can browse/download artifacts under one directory of one repo —
without exposing anything else. Before creating the grant, it checks existing
non-admin users and refuses to continue if another user already has repo-wide
or overlapping path access. Read-only-user resources use a separate priv_share_ prefix
and their own role_share_* roles, so they are invisible to the guest subsystem
and vice versa.
A third use case manages raw/hosted repositories and artifact retention on
Nexus Community/OSS. The CLI safely reconciles repository settings and can
preview or delete old files using last-modified age and path rules. See
doc/raw仓库与制品生命周期PRD.md.
A fourth use case supports warm-standby HA operations for Nexus OSS: two
independent Nexus nodes, one active F5 upstream, periodic blob / metadata
replication, manual fencing, and guided failover. The CLI does not automate F5
or claim zero RPO; it provides dual-node health/status, one-shot sync command
execution, fencing gates, and audit records. See
doc/nexus主从HA模式PRD.md.
When an AI Agent calls nexus-cli, read doc/AI可调用能力清单.md and
doc/AI调用指南.md first, then follow the sequence: inspect state, dry-run /
preview, get human confirmation, execute, and check audit records.
Prebuilt binaries are published with each release. Pick whichever channel fits your environment.
# Install globally.
npm i -g @mogesang/nexus-cli
nexus-cli --help
# Or run it once without a global install.
npx @mogesang/nexus-cli --helpSupported: linux / macOS / Windows on x64 / arm64. The package is a thin
wrapper whose postinstall downloads the matching binary from GitHub
Releases and verifies its sha256.
The yum repo is a static tree served from this project's GitHub Pages and is rebuilt on every release tag. It provides x86_64 and aarch64 packages; all RPMs are GPG-signed.
# Add the repository configuration and import its signing key.
sudo curl -o /etc/yum.repos.d/nexus-cli.repo \
https://mogesangop.github.io/nexus-cli/nexus-cli.repo
sudo rpm --import https://mogesangop.github.io/nexus-cli/RPM-GPG-KEY-nexus-cli
# Install and verify.
sudo dnf install nexus-cli
nexus-cli --help
rpm -q nexus-cli# Add the repository configuration and import its signing key.
sudo curl -o /etc/yum.repos.d/nexus-cli.repo \
https://mogesangop.github.io/nexus-cli/nexus-cli.repo
sudo rpm --import https://mogesangop.github.io/nexus-cli/RPM-GPG-KEY-nexus-cli
# Install and verify.
sudo yum install nexus-cli
nexus-cli --help
rpm -q nexus-cliGrab the archive for your platform from the
latest release,
extract it, and put nexus-cli on your PATH.
make build # produces ./nexus-cli
# or directly:
CGO_ENABLED=0 go build -o nexus-cli ./cmd/nexus-cliThe default
GOPROXYin the Makefile ishttps://goproxy.cn,direct. Override withmake build GOPROXY=https://proxy.golang.org,directif needed.
# 1. Generate a config template. Without --output it lands at
# ~/.nexus-cli/config.yaml (dir created with 0700 if missing).
./nexus-cli config init
# 2. Edit the config: set baseUrl, roleName, and the deny / browseRead
# repository lists. Put protected repos in deny.repositories. Then export
# the admin password:
export NEXUS_ADMIN_PASSWORD='your_password'
# 3. Verify connectivity. --config is optional; if unset the CLI searches
# ./config.yaml, ~/.nexus-cli/config.yaml, /etc/nexus-cli/config.yaml
# (first match wins).
./nexus-cli health check
# 4. Preview the plan (no changes applied).
./nexus-cli guest protect --dry-run
# 5. Apply.
./nexus-cli guest protect
# 6. Verify drift.
./nexus-cli guest check# Dry-run first: prints the selector/privilege/role/user that would be created.
./nexus-cli user create-readonly \
--repo devops-prod-generic \
--path /team-a/ \
--user alice.team-a \
--email alice@example.com \
--first-name Alice --last-name Team \
--dry-run
# Apply. The generated password is printed ONCE to stdout — save it now.
./nexus-cli user create-readonly \
--repo devops-prod-generic \
--path /team-a/ \
--user alice.team-a \
--email alice@example.com \
--first-name Alice --last-name TeamThe grant is idempotent: re-running with the same args reuses the existing
selector, privilege, and role. An existing user is an error — the password
is never reset. user create-readonly supports raw repositories only and fails before
creating anything if another non-admin user already has access to the requested
repo/path. Partial progress is not rolled back, so re-running is safe.
All commands accept an optional --config <path>. When omitted (or --config ""),
the CLI searches ./config.yaml, ~/.nexus-cli/config.yaml, then
/etc/nexus-cli/config.yaml — first existing file wins. An explicit --config
is used verbatim (no search; a typo surfaces as a read error).
| Command | Description |
|---|---|
config init [--output config.yaml] |
Generate a config template (default: ~/.nexus-cli/config.yaml). |
repo list [--format F] [--type T] |
List repositories, optionally filtered by format/type. |
repo get --name R --format F --type T |
Show one repository's full API payload. |
repo apply [--dry-run] [--yes] |
Apply generic repositories declared in repositories.managed. Real changes require --yes. |
repo ensure --name R --format F --type T --settings FILE [--dry-run] [--yes] |
Create or update one generic repository from YAML/JSON settings. Real changes require --yes. |
repo raw apply [--dry-run] [--yes] |
Apply declared raw hosted repositories. Real changes require --yes. |
repo raw ensure --name R --blob-store B [...] [--dry-run] [--yes] |
Create or safely update one raw hosted repository. Real changes require --yes. |
repo lifecycle preview --repo R [...] |
Read-only preview of expired raw components. |
repo lifecycle run --repo R --yes [...] |
Delete expired raw components. |
blobstore list |
List blob stores. |
blobstore get --name B --type file |
Show one file blob store. |
blobstore apply [--dry-run] [--yes] |
Apply file blob stores declared in blobStores.file. Real changes require --yes. |
blobstore ensure --name B --path P [...] [--dry-run] [--yes] |
Create or update one file blob store. Real changes require --yes. |
guest protect [--dry-run] [--yes] [--report FILE] |
Protect guest access from config. Real changes require --yes. |
guest sync [--dry-run] [--yes] [--report FILE] |
Deprecated alias for guest protect. Real changes require --yes. |
guest check |
Read-only check that the guest role matches config. |
user create-readonly --repo R --path /p/ --user U --email E [--dry-run] [--yes] |
Create a user with path-scoped read-only access. Real changes require --yes. |
health check |
Connectivity / API / auth health check. |
ha status |
Show both HA node health plus last blob / metadata sync time and lag. |
ha health |
Run API health checks against both HA nodes. |
ha sync --once [--timeout 30m] |
Execute configured blob and metadata sync commands once and update HA state. |
ha failover --from primary --to standby --fencing-confirmed |
Guide a safe manual failover, optionally run catch-up sync, print F5 steps, and write audit. |
| Flag | Required | Description |
|---|---|---|
--repo |
yes | Repository name. |
--path |
yes | Directory path, must start with /, e.g. /team-a/. |
--user |
yes | User id to create. Must not already exist. |
--email |
yes | User email address. |
--first-name / --last-name
|
no | User display name parts. |
--format |
no | Repository format; auto-detected from repo list if omitted. |
--password-length |
no | Generated password length (default 24). |
--dry-run |
no | Print the plan without creating anything or generating a password. |
See examples/config.example.yaml. Key sections:
-
nexus— connection + credentials.passwordEnvnames the env var holding the admin password (the password is never written to the file). -
repositories.raw— desired raw hosted repositories and CLI retention rules. -
repositories.managed— generic repository desired state for any format/type.settingsis passed through to the Nexus repository API body. -
blobStores.file— desired file blob stores. -
ha— optional warm-standby settings: node pair, replication commands, state file, and manual failover safety gates. -
guestAccess— target role, repository policies, forbidden/warn privileges. -
privilegeNaming— prefix (priv_guest), separator, dash replacement. -
audit— JSONL audit log path and masking. -
report— report directory and format (text|json).
The HA mode follows the product constraint in
doc/nexus主从HA模式PRD.md: Nexus Repository OSS has no native active-active or
primary/standby replication. This CLI therefore implements an operator-guided
warm standby workflow, not synchronous replication.
Add an enabled ha section to config.yaml. Passwords are still read only from
environment variables.
ha:
enabled: true
role: "primary"
nodes:
- name: "primary"
role: "primary"
baseUrl: "http://nexus-a.example.com"
username: "admin"
passwordEnv: "NEXUS_PRIMARY_PASSWORD"
- name: "standby"
role: "standby"
baseUrl: "http://nexus-b.example.com"
username: "admin"
passwordEnv: "NEXUS_STANDBY_PASSWORD"
replication:
stateFile: "./logs/nexus-cli-ha-state.json"
blobSync:
method: "rsync"
schedule: "*/5 * * * *"
command: "rsync -a --delete nexus-a:/nexus-data/blobs/default/ nexus-b:/nexus-data/blobs/default/"
metadataSync:
method: "export-import"
schedule: "*/15 * * * *"
command: "/opt/nexus-ha/sync-metadata.sh"
failover:
mode: "manual"
requireFencing: trueblobSync.command and metadataSync.command are local operator-owned commands
or scripts. They should be idempotent and return non-zero on failure. A typical
metadata script wraps the Nexus Export database task, transfers the completed
export package, then triggers Import database on the standby node.
Export the node passwords before running HA commands:
export NEXUS_PRIMARY_PASSWORD='primary_admin_password'
export NEXUS_STANDBY_PASSWORD='standby_admin_password'nexus-cli ha health --config config.yaml
nexus-cli ha status --config config.yamlha health checks repository, privilege, and guest-role API access on both
nodes. ha status also reads ha.replication.stateFile to show the last
successful blob and metadata sync timestamps, lag, and last error.
nexus-cli ha sync --once --config config.yaml --timeout 45mThe command runs blobSync.command first, then metadataSync.command. It stops
after the first failure and writes the result to the HA state file. Empty sync
commands fail fast with a message telling you which config field to fill.
For scheduled replication, put the actual sync script in cron/systemd timer, or
call nexus-cli ha sync --once from the scheduler after the commands are
configured.
When the primary fails:
# First stop or isolate the old primary so there is no split-brain write path.
# Then run:
nexus-cli ha failover \
--config config.yaml \
--from primary \
--to standby \
--fencing-confirmedBy default ha failover runs a final catch-up sync before printing the F5
switch checklist. If the old primary is hard down and sync cannot run, use
--skip-sync only after accepting the RPO gap:
nexus-cli ha failover \
--config config.yaml \
--from primary \
--to standby \
--fencing-confirmed \
--skip-syncAfter switching F5 so the standby is the only active pool member, verify:
nexus-cli ha status --config config.yaml
nexus-cli guest check --config config.yamlEvery ha sync --once and ha failover attempt writes a JSONL audit record
through the existing audit logger. The record never includes passwords or
authorization headers.
deny > readOnly > browseRead > defaultPolicy
A repository in deny.repositories gets no privilege. In readOnly it gets
read only (hidden from UI, still downloadable). Matching browseRead (and
not excluded) gets browse+read. Otherwise defaultPolicy decides.
Protected repository: hidden and non-downloadable for guest
A protected repository grants no anonymous browse or read. Nexus UI
repository listing and tree browsing depend on browse; exact URL downloads
depend on read. Configure protected repos in deny.repositories:
- Add the repository to
browseRead.excludeRepositoriesso it does not getbrowse+read. - Add the same repository to
deny.repositoriesso it gets no guest privilege.
Example for devops-prod-generic:
guestAccess:
enabled: true
roleName: "role_guest_repository_access"
anonymousUserId: "anonymous"
defaultPolicy: "browseRead"
browseRead:
includeRepositories:
- "*"
excludeRepositories:
- "devops-prod-generic"
readOnly:
repositories: []
deny:
repositories:
- "devops-prod-generic"
actions:
browseRead:
- browse
- read
readOnly:
- readBefore running, make sure role_guest_repository_access exists in Nexus and
the anonymous user (anonymous) has that role. Then apply and check:
export NEXUS_ADMIN_PASSWORD='your_password'
./nexus-cli guest protect --config config.yaml --dry-run
./nexus-cli guest protect --config config.yaml --yes
./nexus-cli guest check --config config.yamlVerify the behavior:
# 1. Open the Nexus UI as anonymous / logged out. devops-prod-generic should
# not appear in the repository list.
# 2. Exact artifact URLs should fail for anonymous / logged-out users.
curl -fL \
'http://nexus.example.com/repository/devops-prod-generic/path/to/artifact.tar'If direct download still works or the repository is still visible in the UI,
the anonymous user probably has another role or privilege that grants access.
guest protect removes broad entries listed in forbiddenPrivileges, but it
does not delete every non-managed role.
priv_guest_{format}_{sanitizedRepo}_{sortedActions} — e.g.
priv_guest_raw_devops_prod_generic_read. Dashes, dots and slashes in the
repo name are replaced with _.
nexus-cli only manages privileges whose name starts with priv_guest_.
Privileges on the role that are not managed are preserved — except
those listed in forbiddenPrivileges (e.g. nx-all, nx-admin,
nx-repository-view-*-*-browse), which are always removed from the guest
role during protect. warnPrivileges (e.g. nx-search-read) are flagged in
guest check but not removed by default.
guest protect is idempotent: a second run with unchanged state creates
nothing and removes nothing. Existing managed privileges that match the config
are skipped; stale managed privileges are removed. guest sync remains as a
deprecated compatibility alias.
repo raw apply is also idempotent and never migrates a blob store or
delete/recreates a conflicting repository. Preview changes and retention first:
./nexus-cli repo raw apply --dry-run
./nexus-cli repo lifecycle preview --repo devops-prod-generic
./nexus-cli repo lifecycle run --repo devops-prod-generic --yesThe lifecycle run can be scheduled with cron. Deleting a Nexus component does not immediately reclaim disk space; Nexus blob-store compaction is still required.
repo apply and blobstore apply are also idempotent. For generic repositories,
the CLI compares the declared settings fields against the live API payload and
allows extra read-only fields returned by Nexus. A repository with the same name
but a different format/type fails instead of being migrated.
- The admin password is read from the environment, never from the config file.
- Audit logs never contain the password or
Authorizationheader. -
--dry-runcomputes and prints the plan without modifying Nexus. - Lifecycle deletion requires explicit
--yes; excluded paths always win.
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 | Wrong admin password | Check NEXUS_ADMIN_PASSWORD. |
| 403 | Account lacks security-management privileges | Use an admin-level account. |
| 404 on a privilege/role endpoint | API path differs in this Nexus minor version | Verify against Nexus UI → Settings → System → API (Swagger). |
| TLS error | Self-signed cert | Set insecureSkipTLSVerify: true or add your CA. |
| Timeout | Slow network / large repo list | Increase nexus.timeoutSeconds. |
API field accuracy: The REST request/response field names used by this CLI follow the standard Nexus 3.76
/service/rest/v1endpoints. Different minor versions may emit different fields; verify against your target instance's Swagger before production use.
make test # unit tests (naming, planner, config) — no network needed
make vet # go vetMaintainer release & distribution setup (npm token, GPG key, GitHub Pages) is documented in
doc/publishing.md.