Skip to content

v1.10.0 — one product, and a support bundle

Choose a tag to compare

@eth-man eth-man released this 01 Oct 07:23
· 11 commits to main since this release
02c4956

One product, and one file to send to support. There is no enterprise layer and no edition any more: LDAP/AD, OIDC, custom roles and audit forwarding are part of the API, always present, and inert until configured (ADR-0027, superseding ADR-0002). A new support bundle collects the console's logs, status and non-secret configuration into one archive, with an opt-in for personal data (ADR-0028). No migration.

Added

Support bundle (ADR-0028). Settings → General → Support bundle downloads one .tar.gz for the last 1, 6, 24 or 72 hours: the API's own logs from every replica, system status, every operational condition with how long it has been open, propagation, non-secret configuration, queue and migration summaries, and audit actions without actors. summary.txt leads with what needs attention — a condition open for six weeks is the first line, not line 90,000. Secrets, identities, client addresses and audit payloads are excluded, and every file is scanned for secret values, private keys, URL credentials, tokens and email addresses before archiving. Needs settings:manage; each export is audited. Also GET /system/support-bundle?hours=N.

Opt-in: configuration and personal data. A per-download tick (&includePersonalData=true) adds the audit trail with actors, addresses and before/after values, the user list with session counts, the full classification including parameter values, and saved queries. The file is named …-with-personal-data.tar.gz, the manifest says so first, and the export's audit row records the choice. Environment secrets, stored credentials, password hashes, tokens and private keys stay out either way.

scripts/support-bundle.sh collects what the console cannot see — container logs, Docker state, host facts, the nexuspuppet-* timers and the journal — on the console VM or a Puppet server, and embeds the console's archive with --include so support gets one file. It never runs docker compose config or a full docker inspect, filters .env through an allow-list, and masks the values of its secrets everywhere.

The API keeps a bounded copy of its own log in the new api-logs volume (/var/log/nexuspuppet): JSON lines, rotated at LOG_FILE_MAX_BYTES (20 MiB) keeping LOG_FILE_KEEP (5) — at most 120 MiB per replica. Stdout is unchanged and remains the log; ADR-0016 is amended to say so. LOG_FILE_MAX_BYTES=0 switches the copy off.

Changed

packages/enterprise moved into apps/api. The directory providers are now apps/api/src/directory/{ldap,oidc}, and forwarding is apps/api/src/audit-forwarding. They are wired in app.module.ts like everything else, and their tests moved with them. ldapts is a dependency of @nexuspuppet/api. Removed with it: the runtime loader, the capability registry, CONTRACTS_VERSION, the descriptor types, EnterpriseLoadError, the CAPABILITIES constant, the ESLint enterprise boundary and the core no-op audit transport. The interfaces and DI tokens in @nexuspuppet/contracts stay, because they keep these testable.

Role editing and audit forwarding are always available. The rbac.custom and audit.export checks are gone. POST/PATCH/DELETE /roles and the /settings/audit/* writes never answer 501. The console's roles table is always editable for settings:manage, and the Syslog and Webhook cards are always real forms.

Directory settings cards key on configuration, not capabilities. Without LDAP_URL or OIDC_ISSUER, the card reads Not enabled and names the variable that enables it. It no longer shows a padlock reading "Enterprise". The deployment card no longer has an Edition row.

Malformed directory or audit-export configuration still stops the API from booting. The message now names the integration: LDAP is configured but its settings are invalid, so the API will not start rather than run without it. …

CI's load-bearing job is renamed from "Core builds without the enterprise layer" to "Build, typecheck, lint, unit tests". It no longer greps for enterprise imports, and it keeps the committed-certificate and private-key checks.

Fixed

After an API restart during a PuppetDB outage, the console said PuppetDB "is not answering and never has". The last successful contact was held only in memory, so a restart erased it. The condition summary, the system status card and the inventory screens' unreachable state now fall back to the newest ManagedNode.projectedAt — a contact that really happened — and say "last successful query …". Production showed "never has" for three weeks of a six-week outage. (#263)

An install that followed DEPLOYMENT.md built an image without LDAP, OIDC, custom roles or audit forwarding. The API image had an EDITION build argument defaulting to core, which left packages/enterprise out, and scripts/deploy.sh never set it. Since 1.9.0 made every feature part of the open-source product, that default meant the documented install path produced the smaller image, and nothing said so — role editing and audit forwarding answered 501 and the directory panels showed as unavailable.

NOTICE said the directory and audit integrations were under a commercial licence and not in this repository. Both stopped being true in 1.9.0.

Removed

The EDITION build argument. Every image now contains the whole product and is installed with npm ci against the committed lockfile — the separate npm install stage and the ldapts reinstall it needed on every rebuild are gone. ldapts loses a stale optional-peer entry and is an ordinary dependency only.

npm run test:e2e:core, npm run test:e2e:enterprise and scripts/dev/e2e-edition.sh, which ran the browser suite with the layer hidden. There is no smaller product left to test.

The NEXUSPUPPET_ENTERPRISE_REPO / _REF block in .env.example, which described a fetch script deleted in 1.9.0.

GET /capabilities. It was a public endpoint that reported an edition, an enterpriseVersion and a list of licensed capabilities, and after ADR-0027 it could only have returned a constant. Use GET /auth/mode for the sign-in sources a deployment offers.

SystemStatus.auditForwarding.available, from GET /system/status. Forwarding is always available. The remaining fields say whether it is on and whether it can send.

Upgrading

EDITION is gone; remove it from .env at your leisure. A leftover line is ignored.

If your .env set EDITION=enterprise, nothing changes. You were already building this image.

If it did not — the default — your next build gains LDAP/AD, OIDC, custom roles and audit forwarding. None of them does anything until it is configured:

  • the LDAP provider is registered only when LDAP_URL is set, and OIDC only when OIDC_ISSUER is;
  • audit forwarding queues and sends nothing until a transport is configured, by AUDIT_EXPORT_URL or from Settings → Integrations;
  • custom roles become creatable by holders of settings:manage; the three built-in roles are unchanged and nobody's access moves until someone defines a role.

Check .env before rebuilding a former core image. LDAP_*, OIDC_* and AUDIT_EXPORT_* were ignored by an image without the layer; they now take effect, and a malformed value stops the API from booting — deliberately, so a deployment that believes it has a directory never silently runs without one:

grep -E '^(LDAP_|OIDC_|AUDIT_EXPORT_)' .env

Anything that calls GET /capabilities now gets 404. Monitoring or scripts that probed it to learn the "edition" should stop. There is nothing to learn. A health check belongs on GET /healthz.

Anything that expected 501 from role or forwarding writes now gets the real answer.

Consumers of GET /system/status lose auditForwarding.available. It was only ever false on a core image.

docker compose up -d creates the api-logs volume, already owned by the API's uid. Nothing to configure. If you replace it with a bind mount, the host directory must be writable by uid 100 (DEPLOYMENT.md, "Support bundles").


Full changelog: v1.9.0...v1.10.0