Commit Graph
100 Commits
Author SHA1 Message Date
josh 34af8e5a8f Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
2026-03-28 20:15:37 -04:00
joshandClaude Sonnet 4.6 76d2bffb4f fix: initialize jobs nav dot on every page load
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Previously the dot only updated when visiting the Jobs page.
Now a jobs fetch runs at bootstrap so the dot reflects status
immediately on any page, including after a hard refresh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:14:53 -04:00
josh 64de0e432c Merge pull request 'fix: queue on-create jobs sequentially and fix history ordering' (#56) from feat/jobs-system into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 28s
Reviewed-on: #56
2026-03-28 20:12:31 -04:00
josh a5b409a348 Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
2026-03-28 20:09:59 -04:00
joshandClaude Sonnet 4.6 8f35724bde fix: queue on-create jobs sequentially and fix history ordering
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
runJobsOnCreate now awaits each job before starting the next,
ensuring they don't stomp each other's DB writes in parallel.

getInstanceHistory changed to ORDER BY changed_at ASC, id ASC so
the creation event (lowest id) is always first regardless of
same-second timestamps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:09:32 -04:00
josh cec82a3347 Merge pull request 'feat: run jobs on instance creation when run_on_create is enabled' (#54) from feat/jobs-system into dev
CI / test (push) Successful in 17s
CI / build-dev (push) Successful in 34s
Reviewed-on: #54
2026-03-28 20:01:53 -04:00
josh 883e59789b Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 17s
CI / build-dev (pull_request) Has been skipped
2026-03-28 20:01:20 -04:00
joshandClaude Sonnet 4.6 817fdaef13 feat: run jobs on instance creation when run_on_create is enabled
CI / test (pull_request) Successful in 18s
CI / build-dev (pull_request) Has been skipped
Jobs with run_on_create=true in their config fire automatically
after a new instance is created. Runs fire-and-forget so they don't
delay the 201 response. Option exposed as a checkbox in each job's
detail panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:00:45 -04:00
josh 372cda6a58 Merge pull request 'chore: bump version to 1.5.0' (#52) from feat/jobs-system into dev
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 38s
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #52
2026-03-28 19:49:19 -04:00
josh 3301e942ef Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
2026-03-28 19:48:48 -04:00
joshandClaude Sonnet 4.6 c4ebb76deb chore: bump version to 1.5.0
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 19:48:16 -04:00
josh bb765453ab Merge pull request 'feat: include job config and run history in export/import backup' (#51) from feat/jobs-system into dev
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 40s
Reviewed-on: #51
2026-03-28 19:44:37 -04:00
josh 88474d1048 Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 17s
CI / build-dev (pull_request) Has been skipped
2026-03-28 19:44:05 -04:00
joshandClaude Sonnet 4.6 954d85ca81 feat: include job config and run history in export/import backup
CI / test (pull_request) Successful in 16s
CI / build-dev (pull_request) Has been skipped
Export bumped to version 3, now includes jobs (with raw unmasked
config) and job_runs arrays. Import restores them when present and
restarts the scheduler. Payloads without a jobs key leave jobs
untouched, keeping v1/v2 backups fully compatible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 19:43:34 -04:00
josh 117dfc5f17 Merge pull request 'feat: add Semaphore Sync job' (#50) from feat/jobs-system into dev
CI / test (push) Successful in 15s
CI / build-dev (push) Successful in 27s
Reviewed-on: #50
2026-03-28 19:35:47 -04:00
josh c39c7a8aef Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 19s
CI / build-dev (pull_request) Has been skipped
2026-03-28 19:35:10 -04:00
joshandClaude Sonnet 4.6 a934db1a14 feat: add Semaphore Sync job
CI / test (pull_request) Successful in 15s
CI / build-dev (pull_request) Has been skipped
Fetches Semaphore project inventory via Bearer auth, parses the
Ansible INI format to extract hostnames, and sets semaphore=1/0
on matching instances.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 19:34:45 -04:00
josh ea4c5f7c95 Merge pull request 'feat: add Patchmon Sync job' (#49) from feat/jobs-system into dev
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 25s
Reviewed-on: #49
2026-03-28 19:24:12 -04:00
josh 5c12acb6c7 Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 17s
CI / build-dev (pull_request) Has been skipped
2026-03-28 19:23:37 -04:00
joshandClaude Sonnet 4.6 0b350f3b28 feat: add Patchmon Sync job
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Syncs patchmon field on instances by querying the Patchmon hosts API
and matching hostnames. API token masked as REDACTED in responses.
seedJobs now uses INSERT OR IGNORE so new jobs are seeded on existing
installs without re-running the full seed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 19:22:41 -04:00
josh db4071a2cf Merge pull request 'fix: move page-jobs inside main so it renders at the top' (#48) from feat/jobs-system into dev
CI / test (push) Successful in 15s
CI / build-dev (push) Successful in 30s
Reviewed-on: #48
2026-03-28 19:15:38 -04:00
josh 37cd77850e Merge branch 'dev' into feat/jobs-system
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
2026-03-28 19:15:07 -04:00
joshandClaude Sonnet 4.6 14a4826bb6 fix: move page-jobs inside main so it renders at the top
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 19:14:32 -04:00
josh 550135ca37 Merge pull request 'feat: jobs system with dedicated nav page and run history' (#47) from feat/jobs-system into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 26s
Reviewed-on: #47
2026-03-28 19:10:50 -04:00
joshandClaude Sonnet 4.6 d7727badb1 feat: jobs system with dedicated nav page and run history
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Replaces ad-hoc Tailscale config tracking with a proper jobs system.
Jobs get their own nav page (master/detail layout), a dedicated DB
table, and full run history persisted forever. Tailscale connection
settings move from the Settings modal into the Jobs page. Registry
pattern makes adding future jobs straightforward.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 19:09:42 -04:00
josh 537d78e71b Merge pull request 'feat: Tailscale sync jobs' (#46) from feat/tailscale-sync-jobs into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 25s
Reviewed-on: #46
2026-03-28 17:12:35 -04:00
joshandClaude Sonnet 4.6 47e9c4faf7 feat: Tailscale sync jobs
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Adds a background job system that polls the Tailscale API on a configurable
interval and syncs tailscale status and IPs to instances by hostname match.

- New config table (key/value) in SQLite for persistent server-side settings
- New server/jobs.js: runTailscaleSync + restartJobs scheduler
- GET/PUT /api/config — read and write Tailscale settings; API key masked as **REDACTED** on GET
- POST /api/jobs/tailscale/run — immediate manual sync
- Settings modal: new Tailscale Sync section with enable toggle, tailnet, API key, poll interval, Save + Run Now buttons, last-run status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 17:11:40 -04:00
josh 31a5090f4f Merge pull request 'fix: remove internal database ID from frontend' (#45) from fix/hide-internal-id into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #45
2026-03-28 16:48:19 -04:00
joshandClaude Sonnet 4.6 ecdac6fe23 fix: remove internal database ID from frontend
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Removed from the instance subtitle and the overview kv grid. The auto-
increment ID is an implementation detail with no user-facing meaning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 16:47:20 -04:00
josh 1a84edc064 Merge pull request 'chore: bump version to 1.4.0' (#43) from chore/bump-v1.4.0 into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 24s
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #43
2026-03-28 16:15:32 -04:00
joshandClaude Sonnet 4.6 bfb2c26821 chore: bump version to 1.4.0
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 16:09:08 -04:00
josh a985268987 Merge pull request 'feat: include history in export/import backup' (#42) from feat/export-import-history into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 21s
Reviewed-on: #42
2026-03-28 16:06:21 -04:00
joshandClaude Sonnet 4.6 218cdb08c5 feat: include history in export/import backup
CI / test (pull_request) Successful in 15s
CI / build-dev (pull_request) Has been skipped
Export now returns version 2 with a history array alongside instances.
Import accepts the history array and restores all audit events. v1 backups
without a history key still import cleanly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 16:04:53 -04:00
josh 2855cc7f81 Merge pull request 'feat: mobile-responsive layout under 640px' (#41) from feat/mobile-responsive into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 23s
Reviewed-on: #41
2026-03-28 15:57:07 -04:00
josh 07d2e215e4 Merge branch 'dev' into feat/mobile-responsive
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
2026-03-28 15:56:38 -04:00
josh 8ef839d6d0 Merge pull request 'fix: wrap image reference in backticks in release notes' (#40) from fix/release-image-codeblock into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 21s
Reviewed-on: #40
2026-03-28 15:55:05 -04:00
joshandClaude Sonnet 4.6 7af88328c8 feat: mobile-responsive layout under 640px
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Single breakpoint, no desktop changes. Key adjustments:
- Reset zoom: 1 (mobile browsers handle scaling)
- Padding drops from 32px to 16px throughout
- Toolbar wraps: search full-width, filters below
- Instance grid and detail grid collapse to single column
- Detail header stacks title above action buttons
- History timeline stacks timestamp above event
- Toggle grid drops from 3 to 2 columns
- Confirm box gets max-width: calc(100vw - 32px) to prevent overflow
- Toast stretches across bottom of screen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:54:12 -04:00
joshandClaude Sonnet 4.6 096e2afb3d fix: wrap image reference in backticks in release notes
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:44:15 -04:00
josh 668e7c34bb Merge pull request 'chore: release v1.3.1' (#38) from chore/bump-v1.3.1 into dev
CI / test (push) Successful in 15s
CI / build-dev (push) Successful in 25s
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #38
2026-03-28 15:40:46 -04:00
joshandClaude Sonnet 4.6 e796b4f400 chore: release v1.3.1
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:40:16 -04:00
josh a4b5c20993 Merge pull request 'fix: clear instance history on delete and import' (#37) from fix/delete-clears-history into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #37
2026-03-28 15:38:15 -04:00
joshandClaude Sonnet 4.6 d17f364fc5 fix: clear instance history on delete and import
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
deleteInstance now removes history rows for that vmid before removing
the instance. importInstances clears all history before replacing
instances. Prevents stale history appearing when a vmid is reused.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:37:45 -04:00
josh 5f79eec3dd Merge pull request 'fix: categorize release notes into New Features / Bug Fixes, drop chores' (#36) from fix/release-notes-format into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 20s
Reviewed-on: #36
2026-03-28 15:36:27 -04:00
joshandClaude Sonnet 4.6 ed98bb57c0 fix: categorize release notes into New Features / Bug Fixes, drop chores
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:35:53 -04:00
josh 074f0600af Merge pull request 'chore: release v1.3.0' (#34) from chore/bump-v1.3.0 into dev
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 21s
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #34
2026-03-28 15:30:02 -04:00
josh e4f9407827 Merge branch 'dev' into chore/bump-v1.3.0
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
2026-03-28 15:29:09 -04:00
josh fde5ce7dc1 Merge pull request 'chore: release v1.3.0' (#33) from chore/bump-v1.3.0 into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 28s
Reviewed-on: #33
2026-03-28 15:28:44 -04:00
joshandClaude Sonnet 4.6 20df10b333 chore: release v1.3.0
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:28:39 -04:00
joshandClaude Sonnet 4.6 c906511bfc chore: release v1.3.0
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:27:30 -04:00
josh 745e5920ad Merge pull request 'fix: set html zoom 1.1 so default scale matches browser 110%' (#32) from fix/base-zoom into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 20s
Reviewed-on: #32
2026-03-28 15:25:32 -04:00
joshandClaude Sonnet 4.6 90e0a98914 fix: set html zoom 1.1 so default scale matches browser 110%
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:24:58 -04:00
josh cba4b73798 Merge pull request 'fix: use badge for stack on detail overview, consistent across all views' (#31) from fix/detail-stack-badge into dev
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 21s
Reviewed-on: #31
2026-03-28 15:22:13 -04:00
joshandClaude Sonnet 4.6 0d567472a9 fix: use badge for stack on detail overview, consistent across all views
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
stack was plain highlighted text on the detail page but a coloured badge
on the home cards and in the history timeline. Now all three use the same
badge component.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:21:25 -04:00
josh 9f6b2ece52 Merge pull request 'fix: parse SQLite timestamps as UTC, not local time' (#30) from fix/sqlite-utc-timestamps into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 20s
Reviewed-on: #30
2026-03-28 15:20:15 -04:00
joshandClaude Sonnet 4.6 e3911157e9 fix: parse SQLite timestamps as UTC, not local time
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
SQLite datetime('now') returns 'YYYY-MM-DD HH:MM:SS' with no timezone
marker. JS was treating this as local time, so timestamps showed the
correct UTC digits but with the local TZ abbreviation attached (e.g.
'7:15 PM EDT' when the real local time was '3:15 PM EDT').

Add parseUtc() which appends 'Z' before parsing any string that has no
existing timezone marker, ensuring JS always treats them as UTC before
the display-timezone conversion is applied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:19:35 -04:00
josh 0589288dfe Merge pull request 'fix: populate nav instance count on direct detail page load/refresh' (#29) from fix/detail-nav-count into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 21s
Reviewed-on: #29
2026-03-28 15:16:32 -04:00
joshandClaude Sonnet 4.6 8ead7687e5 fix: populate nav instance count on direct detail page load/refresh
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
nav-count was only set in renderDashboard, so loading /instance/:vmid
directly left it showing "—". Add getInstances() to the parallel fetch
in renderDetailPage and set the count there too.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:15:55 -04:00
josh 0e1e9b6699 Merge pull request 'fix: show stack badge in history timeline, matching state treatment' (#28) from fix/history-stack-badge into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 22s
Reviewed-on: #28
2026-03-28 15:14:02 -04:00
joshandClaude Sonnet 4.6 3c008c5bce fix: show stack badge in history timeline, matching state treatment
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:13:27 -04:00
josh 1582c28b28 Merge pull request 'fix: clean up instance detail subtitle — dividers, readable values' (#27) from feat/timezone-settings into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 21s
Reviewed-on: #27
2026-03-28 15:10:54 -04:00
josh bcd934f5b1 Merge branch 'dev' into feat/timezone-settings
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
2026-03-28 15:10:26 -04:00
joshandClaude Sonnet 4.6 4c9acd20c7 fix: clean up instance detail subtitle — dividers, readable values
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Separate vmid / id / created with a subtle vertical border so they
don't run together. Bump font to 13px. Labels drop to 11px muted,
values use full --text colour so the actual data stands out clearly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:10:05 -04:00
josh 520fb98d96 Merge pull request 'feat: redesign history timeline — single-line, timestamp right-aligned' (#26) from feat/timezone-settings into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 19s
Reviewed-on: #26
2026-03-28 15:07:01 -04:00
josh 800184d2be Merge branch 'dev' into feat/timezone-settings
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
2026-03-28 15:06:34 -04:00
joshandClaude Sonnet 4.6 82c314f85c feat: redesign history timeline — single-line, timestamp right-aligned
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Each event is now one row: label · old → new on the left, timestamp
right-aligned. Nothing is far from anything else. State changes use the
existing badge component for immediate visual recognition. The created
event reads 'instance created' in accent. Middle-dot separator keeps
field label and change value clearly associated without forced spacing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:06:09 -04:00
josh 2fba532ec7 Merge pull request 'feat: rework history timeline for clarity' (#25) from feat/timezone-settings into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #25
2026-03-28 15:01:39 -04:00
josh 9177578aaf Merge branch 'dev' into feat/timezone-settings
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
2026-03-28 15:01:12 -04:00
joshandClaude Sonnet 4.6 94c4a0af51 feat: rework history timeline for clarity
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Timestamp now sits on its own line above each event so it's visually
separate from the change description. Field names use a friendly label
map (hardware_acceleration → hw acceleration, tailscale_ip → tailscale ip,
etc.). The created event reads "instance created" in accent colour instead
of a raw "created / —". Padding between rows increased.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:00:22 -04:00
josh ec60d53767 Merge pull request 'feat: timezone setting — display timestamps in selected local timezone' (#24) from feat/timezone-settings into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 19s
Reviewed-on: #24
2026-03-28 14:56:01 -04:00
josh ad81d7ace7 Merge branch 'dev' into feat/timezone-settings
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
2026-03-28 14:55:35 -04:00
joshandClaude Sonnet 4.6 badd542bd7 feat: timezone setting — display timestamps in selected local timezone
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Add a Display section to the settings modal with a timezone dropdown.
Selection is persisted to localStorage and applied to all timestamps via
fmtDate (date-only) and fmtDateFull (date + time + TZ abbreviation, e.g.
"Mar 28, 2026, 2:48 PM EDT"). Changing the timezone live-re-renders the
current page. Defaults to UTC.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:53:20 -04:00
josh 7c31ee3327 Merge pull request 'chore: maintenance — test coverage, route cleanup, README rewrite' (#23) from chore/maintenance into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 23s
Reviewed-on: #23
2026-03-28 14:47:27 -04:00
joshandClaude Sonnet 4.6 0ecfa7dbc9 chore: maintenance — test coverage, route cleanup, README rewrite
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
- Add fmtHistVal and stateClass helper tests (7 new, 106 total)
- Add import regression test: missing name field returns 400 not 500
- Fix normalise() crash on missing name: body.name.trim() → (body.name ?? '').trim()
- Extract duplicate DB error handler into handleDbError() helper
- Rewrite README from scratch with audit log, export/import, full API docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:46:48 -04:00
josh f16fb3e088 Merge pull request 'feat: audit log / history timeline on instance detail page' (#22) from feat/history-timeline into dev
CI / test (push) Successful in 17s
CI / build-dev (push) Successful in 22s
Reviewed-on: #22
2026-03-28 14:36:22 -04:00
joshandClaude Sonnet 4.6 cb01573cdf feat: audit log / history timeline on instance detail page
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Adds an instance_history table that records every field change:
- createInstance logs a 'created' event
- updateInstance diffs old vs new and logs one row per changed field
  (name, state, stack, vmid, tailscale_ip, all service flags)
- History is stored under the new vmid when vmid changes

New endpoint: GET /api/instances/:vmid/history

The 'timestamps' section on the detail page is replaced with a
grid timeline showing timestamp | field | old → new for each event.
State changes are colour-coded (deployed=green, testing=amber,
degraded=red). Boolean service flags display as on/off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:35:35 -04:00
josh b48d5fb836 Merge pull request 'fix: remove stacks count from stats bar' (#21) from fix/remove-stacks-stat into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #21
2026-03-28 14:28:19 -04:00
joshandClaude Sonnet 4.6 6e124576cb fix: remove stacks count from stats bar
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Stacks are always just production/development — counting them adds
no useful information to the dashboard summary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:27:43 -04:00
josh 1f328e026d Merge pull request 'fix: uniform 16px spacing above all settings sections' (#20) from fix/settings-section-spacing into dev
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 22s
Reviewed-on: #20
2026-03-28 14:24:09 -04:00
joshandClaude Sonnet 4.6 71c2c68fbc fix: uniform 16px spacing above all settings sections
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Removing the :first-child { padding-top: 0 } override lets every
section use the same padding: 16px 0, so the gap above Export matches
the gap above Import (and any future sections).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:23:14 -04:00
josh 8bcf8229db Merge pull request 'fix: remove top padding from settings modal body' (#19) from fix/settings-modal-body-padding into dev
CI / test (push) Successful in 17s
CI / build-dev (push) Successful in 21s
Reviewed-on: #19
2026-03-28 14:20:20 -04:00
joshandClaude Sonnet 4.6 6e1e9f7153 fix: remove top padding from settings modal body
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
The modal-body's 22px padding-top created a visible gap between the
header divider and the Export section title.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:19:39 -04:00
josh 1fbb74d1ef Merge pull request 'fix: remove top gap above first settings section' (#18) from feat/settings-modal into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 23s
Reviewed-on: #18
2026-03-28 14:16:22 -04:00
josh 617a5b5800 Merge branch 'dev' into feat/settings-modal
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
2026-03-28 14:15:57 -04:00
joshandClaude Sonnet 4.6 0985d9d481 fix: remove top gap above first settings section
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
padding-top on the first .settings-section created a visible gap
above the Export title. Fixed with :first-child { padding-top: 0 }.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:15:25 -04:00
josh 2af6c56558 Merge pull request 'feat: settings modal with database export and import' (#17) from feat/settings-modal into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 21s
Reviewed-on: #17
2026-03-28 14:12:08 -04:00
joshandClaude Sonnet 4.6 af207339a4 feat: settings modal with database export and import
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Adds a gear button to the nav that opens a settings modal with:
- Export: GET /api/export returns all instances as a JSON backup file
  with a Content-Disposition attachment header
- Import: POST /api/import validates and bulk-replaces all instances;
  client uses FileReader to POST the parsed JSON, with a confirm dialog
  before destructive replace

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:10:59 -04:00
josh 20d8a13375 Merge pull request 'chore: bump version to 1.2.2' (#15) from chore/bump-v1.2.2 into dev
CI / test (push) Successful in 14s
CI / build-dev (push) Successful in 24s
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #15
2026-03-28 13:59:53 -04:00
joshandClaude Sonnet 4.6 f72aaa52f8 chore: bump version to 1.2.2
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:59:23 -04:00
josh dd47d5006e Merge pull request 'fix: collapse python3 one-liner to fix YAML indentation error' (#14) from fix/release-yaml-indent into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 21s
Reviewed-on: #14
2026-03-28 13:58:26 -04:00
joshandClaude Sonnet 4.6 10e25e1803 fix: collapse python3 one-liner to fix YAML indentation error
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Multi-line python3 -c "..." had unindented code outside the run: | block,
causing 'yaml: line 83: could not find expected :'. Collapsed to a single
indented line so the YAML parser sees it correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:57:48 -04:00
josh 1a62e2fdd9 Merge pull request 'chore: bump version to 1.2.1' (#12) from chore/bump-v1.2.1 into dev
CI / test (push) Successful in 15s
CI / build-dev (push) Successful in 28s
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #12
2026-03-28 13:54:12 -04:00
joshandClaude Sonnet 4.6 1271c061fd chore: bump version to 1.2.1
CI / test (pull_request) Successful in 16s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:52:40 -04:00
josh 7b2a996c21 Merge pull request 'fix: remove npm cache and fix release notes shell injection' (#11) from fix/release-workflow into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 20s
Reviewed-on: #11
2026-03-28 13:51:49 -04:00
joshandClaude Sonnet 4.6 3233d65db0 fix: remove npm cache and fix release notes shell injection
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped
cache: npm caused ~4min ETIMEDOUT on every run (cache service unreachable).

Commit messages containing backticks were shell-expanded inside the
curl -d "..." string, causing 'sha: No such file or directory'. Fixed by
writing release notes to a temp file and using python3 to build the JSON
payload, then passing it to curl with --data @file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:49:38 -04:00
josh 3037381084 Merge pull request 'chore: bump version to 1.2.0' (#9) from chore/bump-v1.2.0 into dev
CI / test (push) Successful in 13s
CI / build-dev (push) Successful in 26s
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Reviewed-on: #9
2026-03-28 13:22:15 -04:00
joshandClaude Sonnet 4.6 e54c1d4848 chore: bump version to 1.2.0
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:21:18 -04:00
josh 3ae3f98df5 Merge pull request 'fix: use git rev-parse for short SHA in build-dev' (#8) from fix/ci-short-sha into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 20s
Reviewed-on: #8
2026-03-28 13:19:23 -04:00
joshandClaude Sonnet 4.6 65d6514603 fix: use git rev-parse for short SHA in build-dev
CI / test (pull_request) Successful in 12s
CI / build-dev (pull_request) Has been skipped
$GITEA_SHA is unset on Gitea runners — the nav showed "dev-" with an
empty SHA. git rev-parse --short HEAD works regardless of runner env vars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:18:47 -04:00
josh bc44bcbde9 Merge pull request 'fix: remove npm cache from setup-node' (#7) from fix/ci-remove-npm-cache into dev
CI / test (push) Successful in 12s
CI / build-dev (push) Successful in 13s
Reviewed-on: #7
2026-03-28 13:16:33 -04:00
joshandClaude Sonnet 4.6 cae0f2222a fix: remove npm cache from setup-node
CI / test (pull_request) Successful in 13s
CI / build-dev (pull_request) Has been skipped
The Gitea runner's cache service is unreachable, causing a ~4 minute
ETIMEDOUT on every run before falling back to a cold install anyway.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:11:05 -04:00