Skip to content

Releases: deziss/rackmap

v1.0.0 — Operations console: automation, alerting, patching, drift & time-boxed access

Choose a tag to compare

@anshu8858 anshu8858 released this 26 Sep 03:27

RackMap 1.0 turns the inventory into an operations console: cron editing and monitoring, runbooks across the fleet,
pluggable alerting, systemd control, patch management, drift detection and time-boxed access — on PostgreSQL 18,
with the OS-user management bugs fixed and the security gaps found along the way closed.

Upgrading from 0.8.x? Read MIGRATION.md first — see Breaking changes below.

Breaking changes

  • PostgreSQL 18 is the only database. SQLite support is gone. Copy an existing SQLite database across with
    pnpm --filter @inv/api db:migrate:postgres (see MIGRATION.md).
  • Docker Compose runs PostgreSQL and requires POSTGRES_PASSWORD (URL-safe). Set POSTGRES_HOST_PORT if 5432 is
    already taken on the host.
  • The API image runs as the non-root node user. Volumes written by older root-run images must be re-owned
    (chown -R node:node /data) or SSH keys under /data become unreadable.
  • Editing and deleting OS users now needs the remote_os_users license feature, like creating them.
  • Checkout and license activation are admin-only; completing a checkout requires BILLING_MODE=simulated, and
    unverified license keys are refused in production.
  • POST /servers/:id/test-alert needs alertChannel:manage (admin) and only sends a test event.
  • Rebuild the web image: nginx changes fix an empty Servers page on 0.8.0 images and add long timeouts for host
    actions.

Added

  • Cron job editor on each server page (editor+). Lists user crontabs from the spool directory,
    /etc/crontab, /etc/cron.d/*, and systemd timers (read-only). Schedule builder with a plain-English
    description and the next run times in the host's time zone, raw editor, diff preview, and "run now".
    Saves are compare-and-set on the file's hash (409 if it changed on the host) and back the previous
    version up to /var/backups/rackmap-cron on the host. Root, system files, and users that are
    root-equivalent on the host (sudo/wheel/docker/… membership or a sudoers rule) need admin.
  • Cron heartbeat monitoring. "Monitor this job" wraps a crontab entry so the host checks in at
    PUBLIC_BASE_URL/api/v1/ping/<token> with the job's exit code; missed, late, and failing runs raise
    alerts and recover automatically. Heartbeats can also be created by hand for any job that can call a URL.
  • Runbooks. Saved, parameterised scripts run against servers chosen by tag, environment, location, or
    name, with target preview, dry run, per-host output, cancel/rerun, and schedules. Parameters reach the
    script as exported environment variables, never string-substituted. Authoring is admin-only; root runs
    requested by editors wait for an admin, and nobody can approve their own run.
  • Alert channels (Settings → Alerts): Slack, Microsoft Teams, Discord, PagerDuty (incidents open and
    resolve), Telegram, email, and HMAC-signed webhooks, each subscribed to chosen events. Delivery goes
    through a database outbox with retries, backoff, Retry-After handling, and a delivery log. Outbound
    requests are pinned to the resolved IP, never follow redirects, and refuse private, link-local, and
    metadata addresses unless allowed. NOTIFY_WEBHOOK_URL / NOTIFY_TELEGRAM_* keep working and appear as
    read-only channels (the webhook body is unchanged).
  • SSL certificates are scanned daily (SSL_SCAN_CRON) and alert at 30, 14, 7, and 1 days before expiry.
  • systemd Services tab — list units, view details and the journal, and start/stop/restart/reload/enable/disable
    them. Protected units (SSH, networking, D-Bus, Docker, systemd-*, targets, mounts) need an admin; aliases are
    resolved on the host so a protected unit cannot be acted on under another name.
  • Patch management — nightly fleet scan of pending and security updates, reboot-required hosts, and kernels for
    apt/dnf/yum/zypper (PATCH_SCAN_CRON), with a fleet page, per-server card, and admin-only apply.
  • Drift detection — nightly configuration snapshots (DRIFT_SCAN_CRON) compared with an accepted baseline, with
    severity-ranked events, acknowledgement, and drift_detected alerts.
  • Time-boxed access grants — temporary OS accounts and SSH keys revoked automatically at expiry, with host-side
    expiry as a backstop and alerts when a revoke keeps failing.
  • Prometheus service discovery (GET /api/v1/prometheus/sd), new exporter series (heartbeats, runbooks, alert
    deliveries, patches, drift, access grants), and an example scrape config plus Grafana dashboard in contrib/.
  • Scheduled PostgreSQL backups with pg_dump (BACKUP_CRON, BACKUP_KEEP); /health/ready reports the
    last backup.
  • Status probe history stores far less. A row is written only when a server's status changes, or once
    per STATUS_SAMPLE_INTERVAL_MS (default 15 min) otherwise — about 96 rows per server per day instead of
    about 1,440. The table is capped at STATUS_MAX_ROWS (default 10,000) on top of the retention days, and
    admins can see its size and clean it (older than N days, or keep the newest N rows) under
    Settings → Maintenance. On upgrade the first prune trims existing history to the cap.
  • A per-account sign-in limit (AUTH_LOGIN_ACCOUNT_RATE_LIMIT_MAX, default 10/min) alongside the per-IP one.
  • CI runs the test suite against PostgreSQL 18 and fails when migrations drift from the schema.

Fixed

  • Creating, editing, or deleting an OS user hung forever. The request never returned and leaked the SSH
    connection. The commands also ignored the server's SSH password, so they only worked with passwordless
    sudo.
  • Deleting an OS user always failed with 400. The query-string booleans were rejected.
  • When sudo rejects the stored server password (RackMap usually logs in with a key, so a stale password only shows up
    on root actions), the OS-user dialogs ask for the sudo password once and retry; it is sent as X-Sudo-Password
    for that request only and never stored.
  • The Servers page showed no servers on 0.8.0 images (nginx 301-redirected the list to a URL the API does not route),
    and the web container always reported unhealthy.
  • The committed PostgreSQL baseline migration was truncated. It was missing two tables and every foreign
    key and index, and was not valid SQL, so fresh installs could not migrate. If migrate deploy already
    failed on it, see MIGRATION.md.
  • docker compose up could not start: the defaults still pointed at SQLite, and the PostgreSQL volume was
    mounted where the PostgreSQL 18 image refuses to start.
  • Nightly backups silently did nothing on PostgreSQL.
  • ensure-baseline.mjs queried SQLite system tables and silently skipped on PostgreSQL.
  • SSL-expiry emails were never sent (lib/mail.ts only logged). Telegram alerts failed for hostnames
    containing _.
  • The test suite no longer reuses rows across runs, and refuses to run against a database whose name does
    not end in _test.

Security

  • The server's SSH password is no longer placed in the remote command line (it was visible in ps on the
    host). Remote scripts are uploaded over stdin into a private temp file, and sudo is probed before any
    password is sent.
  • Editors can no longer grant sudo or privileged group membership (sudo, wheel, docker, …), change or delete
    root-equivalent accounts, or set a password containing a newline (which injected extra chpasswd lines).
    Password changes are no longer written to the audit log.
  • Creating or editing an OS user without server:sudo is also refused, on the host, when a requested supplementary
    group is root-equivalent there — a group with its own sudoers rule (%deploy ALL=…) or gid 0 — even if it is not on
    the static privileged list.
  • Checkout and license activation are admin-only. Checkout completes only with BILLING_MODE=simulated,
    and in production any-key license activation is refused without a license server.
  • POST /servers/:id/test-alert requires alertChannel:manage and sends a test event only.

Dependencies

  • Resolved the open Dependabot alerts: vitest 4.1 (test runner config migrated), deepmerge-ts 8 via a pnpm override
    (Prisma 6.19 pins 7.x), and a single zod 4.6 across packages; better-auth, nodemailer, hono, postcss, browserslist
    and nanoid were already on patched versions.
  • Radix UI, TanStack Router, @vitejs/plugin-react and jspdf-autotable 5 (PDF export moved to the functional API).
  • GitHub Actions: actions/checkout v7, docker/metadata-action v6, docker/setup-qemu-action v4,
    docker/setup-buildx-action v4, docker/build-push-action v7.
  • Docker images stay on Node.js 24 LTS; Dependabot now skips Node majors until the next even release reaches LTS.

Changed

  • PostgreSQL 18 is the only supported database. Compose runs it by default and requires POSTGRES_PASSWORD.
  • New server:cron, alertChannel, heartbeat, and runbook permissions; license features
    remote_cron and runbooks (Pro and Enterprise).

Full changelog: v0.8.0...v1.0.0

v0.8.0 — Multi-replica support & dependency remediation

Choose a tag to compare

@anshu8858 anshu8858 released this 21 Sep 03:02
2998e93

Closes the three items left open by 0.7.0, and fixes two problems found while doing them.

🔁 Multi-replica support

Background jobs now take a database-backed lease before running, so the scheduler, the metrics alert sweep and the nightly backup execute on exactly one replica.

Until now the only thing standing between you and double-probing the entire fleet — and duplicate notifications, and two SQLite backups racing each other — was instances: 1 in the PM2 config. docker compose up --scale api=2 broke it immediately.

A holder killed mid-job recovers automatically once its lease expires (JOB_LOCK_TTL_MS, default 120s), with no operator cleanup. The lease is renewed on a heartbeat so a long sweep against a large fleet cannot lose it mid-run.

Verified under a 25-way stampede: exactly one winner in the insert race, exactly one in the expired-lease race, zero winners against a live lease, and 25 impostors all failed to release it. A lock that is only usually exclusive would be worse than none — the failure would be intermittent and nearly undiagnosable.

🔑 Host-key administration

GET /api/v1/ssh-host-keys (editor+) and DELETE /api/v1/ssh-host-keys/:id (admin, audited with the discarded fingerprint).

0.7.0 documented a migration to SSH_HOST_POLICY=tofu that was not practical: reviewing what had been pinned meant sqlite3 inside the container, and every legitimate rebuild meant deleting a row by hand. The listing also flags sharedWithOtherEndpoints — normal for a cluster built from one image, worth investigating otherwise.

🔐 Legacy credentials upgrade themselves

A secret still sealed in the pre-0.7 v1 envelope is re-encrypted to v3 when its row is written for another reason. Never on read, never over a value the request is itself supplying, and never for a vault-wrapped v2 blob. A failed upgrade leaves the stored credential untouched.

📦 Dependencies

24 of 27 Dependabot alerts cleared: hono 4.12.25 → 4.13.8 (six advisories), better-auth 1.6.18 → 1.6.22 (high), nodemailer 9.0.1 → 9.1.1 (high), @hono/node-server → 1.19.17, and transitively nanoid → 3.3.19, postcss → 8.5.28, browserslist → 4.29.0.

🐛 Two bugs this surfaced

2FA verification would have failed at runtime. better-auth 1.6.21 added an account lockout that is enabled by default (10 attempts, 15 minutes) and writes failedVerificationCount, lockedUntil and verified on every verification attempt. The TwoFactor model had none of those columns. The test suite does not cover the 2FA verify path, so a green suite did not clear this — it was found by reading the installed package.

Container start would have aborted on upgrade. The 0.7.0 adoption step reconciles a pre-existing database with db push, which creates every table including ones belonging to later migrations, but then recorded only the baseline as applied. migrate deploy would then fail trying to create a table that already existed, exit non-zero, and the container's start chain would never reach the application. Adoption now records the full migration history.

Also fixed

  • A service's auth token could never be updated — authToken was not destructured in updateService, so it reached Prisma as an unknown field and the update threw, despite the schema accepting it.
  • Rate limiting collapsed to one shared bucket behind two or more proxy hops. better-auth refuses to guess which hop is the client and returns no IP for a multi-value X-Forwarded-For; every caller then shared a single key. trustedProxies now defaults to loopback plus the RFC1918 ranges, overridable with TRUSTED_PROXY_CIDRS.
  • nginx: the 24-hour read timeout is scoped to the SSH WebSocket path rather than every API request; Connection: upgrade is only sent when the client asks (it was breaking keep-alive on every ordinary request); client_max_body_size raised to 25 MB so XLSX imports are not rejected at nginx's 1 MB default.
  • compose: the healthcheck targets /health/ready instead of /health/live, which returned a literal ok and could never fail; added start_period so first-boot migrations do not exhaust the retries; the web container has a healthcheck; both rotate logs; Postgres binds to loopback rather than every interface.
  • The test suite was tripping better-auth's own sign-in limiter — 15 logins in one shared process against a cap of 10 per minute — so suites intermittently failed to collect with 429, hitting a different victim each run.

Upgrading

git pull
docker compose up -d --build

Migrations are applied automatically, including adoption of databases created by pre-0.7 images. Take a backup first, as always.

To actually run more than one API replica, scale after upgrading — the lease makes it safe:

docker compose up -d --scale api=2

Verification

pnpm build, pnpm -r typecheck and pnpm test all pass. 258 tests, up from 208 in 0.7.0 and 35 before this work began.

Both migration paths were re-verified against a copy of a real 125-server database: fresh install applies cleanly, an existing database adopts with all rows intact and exits zero, and a second run is a no-op.

Still open

  • deepmerge-ts (high) is pinned exactly by @prisma/config@6.19.3; reaching the fixed version requires a Prisma major upgrade.
  • vitest 3 → 4 (medium, devDependency) removes poolOptions, which this project relies on to keep every spec in one process against a shared SQLite test database. Deferred rather than destabilise the suite at release time.

Full changelog: https://github.com/deziss/rackmap/blob/v0.8.0/CHANGELOG.md

v0.7.0 — Machine auth, host-key verification & automation

Choose a tag to compare

@anshu8858 anshu8858 released this 20 Sep 20:41

Completes the remediation started in 0.6.1, and makes RackMap usable as a source of truth for automation.

Some deployments need configuration changes — see Action required.

🔑 API keys actually authenticate

The apiKeyAuth middleware has existed since 0.5 and was never mounted, so every key minted since then authenticated nothing. It is now wired to every /api/v1 route.

curl -H "Authorization: Bearer sk_..." https://rackmap.example.com/api/v1/servers

Keys carry a role ceiling (scopeRole, defaulting to viewer) and an optional expiry. A key can never exceed its creator's role, and it is re-capped at request time against the owner's current role — so demoting or banning a user immediately demotes their keys. Disabled, revoked and expired keys are rejected.

🔒 SSH host-key verification

Host keys are pinned on first contact and compared on every later connection. Verification runs during key exchange, before any credential is offered, so a changed key cannot harvest your password — which mattered, because the auth fallback answers every keyboard-interactive prompt with the decrypted password.

SSH_HOST_POLICY=tofu refuses a changed key. The accept-any default pins and warns loudly but still connects, so an existing fleet can populate the store without an outage. A mismatch never overwrites the stored key — self-healing would erase the evidence.

Fingerprints are standard OpenSSH SHA256: values, so they compare directly against ssh-keygen -lf.

🛡️ Vault rotation no longer destroys data

POST /api/v1/vault/reset now takes { currentPassphrase, newPassphrase } and re-wraps the existing data-encryption key, preserving every stored credential. The old behaviour — mint a new key and orphan everything — requires an explicit forceDestroy: true, and the UI puts it behind a separate checkbox.

A request supplying neither is rejected. The API will not guess which one you meant.

🔐 New encryption envelope

Secrets are now sealed as v3. with a random per-secret salt and a scrypt-derived key, replacing an unsalted, single-round SHA-256 derivation that was fully offline-attackable against an exfiltrated database.

Existing v1. data still decrypts. No migration, no re-encryption, no downtime. The derived key is cached so the SSH path does not pay the KDF cost per connection.

🤖 Automation

  • Prometheus exporter at /api/v1/metrics — counts by status, per-host up/down and probe latency, probe staleness (a rising rackmap_server_last_probe_age_seconds means the scheduler stopped), GPU counts, certificate expiry.
  • Ansible dynamic inventory — contrib/rackmap-inventory.py, grouping by environment, provider, location, type, owning team, tag, status and GPU presence. No dependencies beyond the standard library.
export RACKMAP_URL=https://rackmap.example.com RACKMAP_API_KEY=sk_...
ansible -i contrib/rackmap-inventory.py gpu -m ping

🚦 Brute-force protection

Password reveal is limited to 5 per record and 20 per user per 5 minutes; the SSH credential test to 10 per host and 30 per user. The per-user ceiling is the one that matters — it turns "script a loop and dump the fleet" into hours of work and hundreds of audit rows.

The WebSocket terminal now caps password attempts per socket instead of allowing unlimited retries for an hour, and re-checks authorization on an interval so a live root shell closes on ban, role downgrade or access-request expiry.

🗄️ Migration history

The previous history was missing seven tables, so db push (Docker) and migrate deploy (systemd) built different schemas. History is squashed to a single baseline and containers now run migrate deploy.

Pre-existing databases are adopted automatically on first start — the schema is reconciled with db push without --accept-data-loss (so it adds and never drops), then the baseline is recorded. No manual step.

Verified against a copy of a real 125-server database: all rows intact, missing columns added, idempotent on re-run. A fresh database builds all 28 tables from the baseline alone.

Also fixed

  • Soft-deleted servers were still being SSH-polled every five minutes.
  • A failure in the server sweep also skipped the service sweep and the history prune.
  • Metrics-check failures were swallowed entirely — a server with rotated credentials silently stopped being checked, with nothing in the logs.
  • A WebSocket that never reached a shell had no maximum-duration cap, and any inbound traffic reset its idle timer.
  • Locking your own vault session no longer stops every background job.
  • X-Forwarded-For is only honoured behind TRUST_PROXY; it previously set the audit IP and rate-limit bucket unconditionally.
  • Bans and role changes take effect on the next request rather than up to five minutes later.
  • Containers run as non-root, install from a frozen lockfile, and seed idempotently. They still carry devDependencies — pruning them breaks Prisma client resolution under pnpm, and a larger image beats a broken one.

Action required on upgrade

  1. TRUST_PROXY defaults to false. Behind a reverse proxy, set TRUST_PROXY=true or every audit entry records the proxy's address. The bundled docker-compose.yml sets it for you.
  2. Existing API keys have no scopeRole and inherit their owner's role. Re-mint any automation key with scopeRole: "viewer".
  3. For strict host-key checking, run on accept-any until every host has been contacted at least once, review what was pinned, then set SSH_HOST_POLICY=tofu. See the README.
git pull
docker compose up -d --build

Take a database backup first, as with any release that touches migrations.

Verification

pnpm build, pnpm -r typecheck and pnpm test all pass. 208 tests, up from 203 in 0.6.1 and 35 before this work started.

Still open

  • Metrics are still not persisted as a time series — that is deliberate; use the Prometheus exporter.
  • No scheduler locking, so running more than one API replica will double-probe.
  • 27 Dependabot alerts remain on the default branch.

Full changelog: https://github.com/deziss/rackmap/blob/v0.7.0/CHANGELOG.md

v0.6.1 — Security release

Choose a tag to compare

@anshu8858 anshu8858 released this 20 Sep 19:44
e36dda8

Security release. Upgrading is recommended for all deployments.

Several endpoints were missing authorization checks, several request fields that reach shell commands on managed hosts were unvalidated, and several defaults were permissive. This release fixes all of them.

No API or database schema changes — but some deployments need configuration changes. See Action required below.

A GitHub Security Advisory with full detail will follow shortly.

Authorization

Endpoints that were reachable by any authenticated user — including a viewer — now require an appropriate role:

  • GET /servers/:id/auto-update — executes a shell script on the managed host over SSH
  • POST /ssh-keys/test-server/:id — an online SSH credential test
  • GET /ssh-keys — host key listing
  • GET /servers/:id/alert-channels — notification configuration
  • All six SSL mutations (scan, per-domain scan, create, update, delete, restore)

Global vault unlock and lock now require admin-level vault permissions. They were gated on the server-update permission, which an editor holds, despite being documented as admin-only. Persisting the vault passphrase to .env requires a separate permission and is now recorded in the audit log.

Input validation

Request fields interpolated into shell commands are now strictly validated and shell-escaped:

  • ATOP time windows are pinned to HH:MM[:SS], enforced in the shared schema and re-checked at the point of interpolation
  • OS user shells, home directories and group names are validated and escaped
  • Sudoers command entries are allowlisted to absolute command paths with plain arguments
  • Sudoers rules are written from a base64 payload via mktemp and install(1) instead of an echo redirect, removing both shell expansion and a predictable-filename race

Two related bugs fixed along the way: install -o root -g root replaces mv + chmod, which previously left sudoers.d files owned by the SSH user — files sudo silently ignores. And a custom sudo grant with an empty command list used to fall back to ALL, silently granting unrestricted passwordless root; it is now rejected.

Defaults

  • TRUSTED_ORIGINS falls back to WEB_ORIGIN instead of *. The wildcard remains available as an explicit opt-in and logs a warning at boot.
  • The Secure cookie flag is set whenever BETTER_AUTH_URL or WEB_ORIGIN is https, rather than being inferred from BETTER_AUTH_URL alone.
  • Self-registration is disabled unless ALLOW_SELF_SIGNUP=true.
  • The "persist passphrase to .env" checkbox defaults to off. The write is now atomic, mode 0600, and anchored to the exact variable so it no longer rewrites commented-out or similarly-named lines.

Audit coverage

Added entries for fifteen mutations that were previously unrecorded: SSH key add/remove, SSH connectivity tests, API key issue/revoke, access-request deletion, remote auto-update changes, storage recalculation, all SSL mutations, and all four inventory export endpoints.

Documentation accuracy

The credential vault was described as zero-knowledge with client-side WebCrypto encryption in fourteen places across the README, user guide, security policy and product portal. No client-side cryptography exists. The vault is server-side envelope encryption (PBKDF2 → KEK → DEK, AES-256-GCM): the passphrase is sent to the server on unlock, and opting in to auto-unlock writes it to .env. All affected copy now describes the actual design.

Run RackMap behind TLS.

Also corrected: SSH_ENABLED was documented as an RCE kill-switch. It gates only the browser terminal — metrics, discovery, log viewing, ATOP and OS user management still execute commands over SSH when it is false. And SSH_HOST_POLICY is declared but never read; it is now marked as not implemented. Real host-key verification is next.

Action required on upgrade

  1. TRUSTED_ORIGINS no longer defaults to *. If you reach RackMap at a hostname or IP that differs from WEB_ORIGIN, sign-in will be rejected. Set it explicitly:
    TRUSTED_ORIGINS=https://rackmap.example.com,http://10.0.0.5:8080
    
    TRUSTED_ORIGINS=* restores the old behaviour and logs a warning.
  2. ALLOW_SELF_SIGNUP defaults to false. The registration form and the checkout sign-up step are hidden. Set it to true to restore self-registration.
  3. Custom sudo permissions with an empty command list are rejected. Supply explicit absolute command paths, or choose the full-access option deliberately.

Upgrading

git pull
docker compose up -d --build

Verification

pnpm build, pnpm -r typecheck and pnpm test all pass. Test count goes from 35 to 150: the new suites assert that an unauthenticated caller and a viewer are both rejected on every newly guarded route, and that hostile values are rejected on every field that reaches a shell.

Still open

  • Host-key verification is not implemented — hostVerifier currently accepts any key. Planned for the next release.
  • 27 Dependabot alerts (8 high) remain on the default branch.

Full changelog: https://github.com/deziss/rackmap/blob/v0.6.1/CHANGELOG.md

v0.6.0 — Public release prep

Choose a tag to compare

@anshu8858 anshu8858 released this 20 Sep 18:44

RackMap is agentless infrastructure inventory and monitoring. Track servers, services and SSL certificates; stream live CPU, RAM, disk and GPU metrics over plain SSH with nothing installed on target hosts.

This release makes the project ready for public use. No API or database schema changes — upgrading from 0.5.0 requires no migration.

RackMap dashboard

Highlights

📖 Documentation rebuilt

  • README restructured around a 60-second quick start, a documentation index, and collapsible feature sections
  • Encryption and credential vault documented as a two-tier guide with an unlock-mode comparison
  • SMTP, metrics alerting, and alert threshold variables documented for the first time

🤝 Community health files

  • CONTRIBUTING.md — dev setup, branch and commit conventions, PR checklist
  • SECURITY.md — private vulnerability reporting, disclosure timeline, and an operator hardening checklist
  • CODE_OF_CONDUCT.md — Contributor Covenant 2.1
  • CHANGELOG.md — history back to 0.2.0
  • Issue templates, a PR template, and Dependabot for npm, Actions, and Docker

🔧 CI fixed

CI had been failing on every run since it was introduced. apps/web/src/routeTree.gen.ts is generated by the TanStack Router Vite plugin at build time and is intentionally not committed, but CI ran typecheck before build, so tsc could never resolve the module. The build step now runs first. The CI badge is green for the first time.

📝 Documentation accuracy

  • End-to-end test command is pnpm e2e, not the non-existent pnpm test:e2e
  • systemd unit is server-inventory.service, not rackmap.service
  • Prerequisites are Node.js 22+ and pnpm 10+, matching CI
  • Tech stack lists React 19, not React 18
  • Quick start uses the real clone URL instead of a placeholder

🔒 Sanitization

  • A real tenant domain replaced with example.com in the user guide, an SSL checker comment, and a UI placeholder
  • Internal planning documents containing production IP addresses removed from the repository and ignored going forward
  • .gitignore hardened to exclude every .env variant except .env.example, private keys and certificates, database files, and test artifacts
  • The dashboard screenshot was captured from a live instance with all hostnames, IP addresses, team names, and account names masked

Upgrading

git pull
docker compose up -d --build

Known issues

Dependabot reports 27 vulnerabilities in dependencies (8 high, 18 moderate, 1 low) on the default branch. The Dependabot configuration added in this release will start opening grouped update PRs; dependency remediation is planned for 0.6.1.

Full changelog: https://github.com/deziss/rackmap/blob/v0.6.0/CHANGELOG.md