Releases: deziss/rackmap
Release list
v1.0.0 — Operations console: automation, alerting, patching, drift & time-boxed access
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). SetPOSTGRES_HOST_PORTif 5432 is
already taken on the host. - The API image runs as the non-root
nodeuser. Volumes written by older root-run images must be re-owned
(chown -R node:node /data) or SSH keys under/databecome unreadable. - Editing and deleting OS users now needs the
remote_os_userslicense 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-alertneedsalertChannel: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-cronon 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-Afterhandling, 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, anddrift_detectedalerts. - 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 incontrib/. - Scheduled PostgreSQL backups with
pg_dump(BACKUP_CRON,BACKUP_KEEP);/health/readyreports the
last backup. - Status probe history stores far less. A row is written only when a server's status changes, or once
perSTATUS_SAMPLE_INTERVAL_MS(default 15 min) otherwise — about 96 rows per server per day instead of
about 1,440. The table is capped atSTATUS_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 asX-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. Ifmigrate deployalready
failed on it, see MIGRATION.md. docker compose upcould 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.mjsqueried SQLite system tables and silently skipped on PostgreSQL.- SSL-expiry emails were never sent (
lib/mail.tsonly 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
pson 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 extrachpasswdlines).
Password changes are no longer written to the audit log. - Creating or editing an OS user without
server:sudois 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-alertrequiresalertChannel:manageand sends a test event only.
Dependencies
- Resolved the open Dependabot alerts: vitest 4.1 (test runner config migrated),
deepmerge-ts8 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-reactandjspdf-autotable5 (PDF export moved to the functional API). - GitHub Actions:
actions/checkoutv7,docker/metadata-actionv6,docker/setup-qemu-actionv4,
docker/setup-buildx-actionv4,docker/build-push-actionv7. - 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, andrunbookpermissions; license features
remote_cronandrunbooks(Pro and Enterprise).
Full changelog: v0.8.0...v1.0.0
v0.8.0 — Multi-replica support & dependency remediation
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 —
authTokenwas not destructured inupdateService, 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.trustedProxiesnow defaults to loopback plus the RFC1918 ranges, overridable withTRUSTED_PROXY_CIDRS. - nginx: the 24-hour read timeout is scoped to the SSH WebSocket path rather than every API request;
Connection: upgradeis only sent when the client asks (it was breaking keep-alive on every ordinary request);client_max_body_sizeraised to 25 MB so XLSX imports are not rejected at nginx's 1 MB default. - compose: the healthcheck targets
/health/readyinstead of/health/live, which returned a literalokand could never fail; addedstart_periodso 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 --buildMigrations 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=2Verification
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.vitest3 → 4 (medium, devDependency) removespoolOptions, 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
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/serversKeys 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 risingrackmap_server_last_probe_age_secondsmeans 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-Foris only honoured behindTRUST_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
TRUST_PROXYdefaults tofalse. Behind a reverse proxy, setTRUST_PROXY=trueor every audit entry records the proxy's address. The bundleddocker-compose.ymlsets it for you.- Existing API keys have no
scopeRoleand inherit their owner's role. Re-mint any automation key withscopeRole: "viewer". - For strict host-key checking, run on
accept-anyuntil every host has been contacted at least once, review what was pinned, then setSSH_HOST_POLICY=tofu. See the README.
git pull
docker compose up -d --buildTake 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
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 SSHPOST /ssh-keys/test-server/:id— an online SSH credential testGET /ssh-keys— host key listingGET /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
mktempandinstall(1)instead of anechoredirect, 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_ORIGINSfalls back toWEB_ORIGINinstead of*. The wildcard remains available as an explicit opt-in and logs a warning at boot.- The
Securecookie flag is set wheneverBETTER_AUTH_URLorWEB_ORIGINis https, rather than being inferred fromBETTER_AUTH_URLalone. - 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
TRUSTED_ORIGINSno longer defaults to*. If you reach RackMap at a hostname or IP that differs fromWEB_ORIGIN, sign-in will be rejected. Set it explicitly:TRUSTED_ORIGINS=https://rackmap.example.com,http://10.0.0.5:8080TRUSTED_ORIGINS=*restores the old behaviour and logs a warning.ALLOW_SELF_SIGNUPdefaults tofalse. The registration form and the checkout sign-up step are hidden. Set it totrueto restore self-registration.- 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 --buildVerification
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 —
hostVerifiercurrently 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
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.
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 checklistSECURITY.md— private vulnerability reporting, disclosure timeline, and an operator hardening checklistCODE_OF_CONDUCT.md— Contributor Covenant 2.1CHANGELOG.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-existentpnpm test:e2e - systemd unit is
server-inventory.service, notrackmap.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.comin 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
.gitignorehardened to exclude every.envvariant 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 --buildKnown 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
