Stop dropping peer deletes when the synced snapshot is empty (e.g. last
vhost removed with syncServers off). Trust Autopass LWW instead of
rejecting lower stateRevision, avoid bumping revision on device-name
status pushes, and retry push after sync warmup if Autopass was not open yet.
Default sync omitted servers as [], which made every remote apply delete
local servers and later saves persist the empty list. Omit unsynced
collections, serialize apply/push, reject stale revisions, and write
state.json atomically with backup recovery.
Bump host stack (autopass, corestore, bare-ws/https/tls/path) and bare-build 1.x; disable TLS verification for tunnel backends and pass device name into Autopass.pair.
Restoring or blurring focus before setting aria-hidden on the backdrop
avoids the browser warning when a control inside the modal (e.g. SSH
Disconnect) still had focus.
Remove folder-level package.json files from dashboard data directories in both repos.
Those overrides caused esm-wrap parsing of plain script data files
(tlds.js, hostname-validator.js, install-commands.js), triggering
"Unexpected token 'export'" at runtime.
- Add syncServers setting (default false) to extension and native host state
- Add "Sync Servers" toggle under Settings → Sync; when disabled, server
tunnels stay local and are not synced across linked devices
- On push: omit servers from snapshot when syncServers is false
- On apply: keep local servers and nextServerId when syncServers is false
(incremental and full restore)
- Stop calling closeSyncForBackup() during backup create so sync and
autopass keep running; backup already excludes autopass/ from the
archive.
- Restore still quiesces sync and removes autopass/ so storage can be
replaced safely; sync reconnects on next use.
- Clarify setSyncManager comment (restore-only). Update BACKUP.md,
SYNC.md, and ARCHITECTURE.md accordingly.
When a device pairs with the master, the master's autopass can emit an update
and applyRemoteUpdate would sometimes apply an empty or weaker snapshot from the
merged view, overwriting the master's state. Skip applying remote state when
we're the master and the incoming snapshot has less content (servers + vhosts +
serviceTunnels) than current state.
- Delink: any peer can leave; removeWriter notifies others, local state reset to defaults.
- Disband: master only; push syncGroupDisbanded so peers reset; master exports state to
state.json, then reloads (cleanup + restorePersistedState + restorePersistedTunnels).
- Skip handleDisbandFromMaster when receiver is master so master state is not reset.
- Store syncMasterDeviceId in synced state so MASTER badge shows on all devices.
- UI: show Delink for peers, Disband for master only; confirm modals for both.
- state.js: add lastLoadedDeviceNames cache, getDeviceNames(),
setDeviceNames(); saveStateSync() always merges and writes
deviceNames so saves never strip them.
- holesail-manager: include deviceNames in saveState() and
getStateSnapshot(); applySnapshotData() updates cache from
synced snapshot. buildDefaultState and legacy migration
include deviceNames: {}.
Prevents hostnames reverting to "Device 1" etc. after saves or sync.
When reopening with existing identity, do not pass discovery key to
Autopass. That makes Autobase bootstrap from this device's local core
(name: 'local') instead of bootstrap.getUserData('autobase/local'),
which can point at another device's writer and open it read-only.
- Device that creates sync group is the Master; MASTER label in Linked devices table
- Create invite when already linked generates invite for same sync group (no wipe)
- Any peer can create invites so devices can be added when Master is offline
- Identity file stores isMaster; same sync group reused across native host restarts
- Update SYNC.md, NATIVE-HOST.md, ARCHITECTURE.md, CHANGELOG.md
- Add getStateSnapshot, applySnapshotData, setStateSaveSuppressed to holesail-manager
- Sync diffs snapshot vs current state and only removes/adds/updates changed tunnels
- Suppress saves during apply and write state.json once at the end
- Fall back to full cleanup + restore when incremental APIs are unavailable
- Backup: do not include autopass/ (may be in use); always include
autopass-identity.json so sync identity is never lost
- Restore: remove autopass/ after extract so autopass can recreate from
restored identity
- Docs: BACKUP.md, SYNC.md, ARCHITECTURE.md updated
- pairWithInvite: await store.ready() before pair; await pass.ready()
after pair.finished(); on error call pair.close() then closePass()
- createSyncInvite: on error await closePass() instead of nulling refs
- Remove autopass dir before createSyncInvite/pairWithInvite so Corestore
gets a fresh device-file (device-file throws when inode/mtime changes)
- Await sync/rdp/holesail cleanup on host shutdown so corestore closes
before process exit
- Reset initPromise on init failure so Create invite can retry after
removing corrupt autopass dir
- getSyncStatus: fire-and-forget push device hostname when missing so
linked peers see names after opening Sync page
- Backup/restore: close sync (corestore) before copy/extract so autopass
data is consistent; backup includes autopass/ and autopass-identity.json
- Installer: preserve sync data on re-run (comments and messages)
- Docs: BACKUP.md sync-in-backup and pause behaviour; SYNC.md backups
include sync data
- Sync: use OS hostname for each device in Linked devices table; persist
deviceNames in state and merge on save/apply so peers see each other's names
- Docs: expand SYNC.md (linked devices table, replace-state modal, multi-device,
offline); NATIVE-HOST getSyncStatus (deviceId, syncGroupId, linkedDevices,
deviceName/name); ARCHITECTURE sync-manager and autopass paths; README
features/dashboard/file locations/doc link; BACKUP and SECURITY cross-refs
- getSyncStatus() now iterates pass.base.activeWriters and returns
linkedDevices: [{ id, isCurrent }] for each writer in the sync group
- Dashboard Linked devices table shows every device: "This device" plus
"Device 2", "Device 3", etc. with their IDs; rows sorted by id
- Fallback to single "This device" row when activeWriters is missing
- Await restorePersistedTunnels so applyingSync stays true for full restore,
preventing saveState() during tunnel startup from pushing back and looping
- Debounce onRemoteUpdate (600ms) and skip apply when state unchanged
(canonical fingerprint) to avoid redundant restarts
- Add confirm modal before "Link device": warns that current state will be
replaced and suggests creating a backup first
- Add Linked devices table when linked: show This device ID (writerKey) and
Sync group ID (discoveryKey); getSyncStatus returns deviceId and syncGroupId
- Await restorePersistedTunnels in applySyncedState so applyingSync stays
true for the full restore. Prevents saveState() during tunnel startup
from pushing back to autopass and causing a feedback loop.
- Debounce onRemoteUpdate (600ms) so bursts of autopass 'update' events
trigger a single apply.
- Skip apply when remote state matches current state (canonical fingerprint)
to avoid redundant restarts and re-pushes.
- Clear remoteUpdateDebounceTimer in cleanup().
- Add install-command helper (getInstallCommand + platform detection)
with OS-specific one-liners and configurable base URL
- Show onboarding card on Overview when !hostConnected: copy button,
platform switcher (macOS/Linux / Windows), and "Check again" button
- "Check again" triggers refresh and shows success/disconnected toast
- refresh() returns state so callers can react to hostConnected
- Add unit tests for install-commands (all platforms, base URL)
- Document "Native host not found" flow in README and INSTALLATION
- CI: rename Test step to Unit tests
- README: add Testing and linting subsection, link to Contributing
- CONTRIBUTING: expand Testing with test layout table and CI note
- ARCHITECTURE: add Testing section
- INSTALLATION: add npm test to build-from-source steps
- Add unit tests (hostname-validator, TLDs, payload-schemas) and integration tests for message handler registry
- Refactor native host message router into handler registry (handlers/state, tunnels, ssh, rdp, backup, ca, connections)
- Add ESLint config and npm test + lint steps in CI
- Dashboard: visibility-based refresh pause, configurable refresh interval (2s/5s/10s/paused)
- Accessibility: ARIA on nav and modals, focus trap and restore, prefers-reduced-motion
- Empty states: primary action buttons for virtual hosts, servers, service tunnels
- Native host rate limiting for backup and CA operations; update SECURITY.md
- CONTRIBUTING: "Adding a new dashboard page", dev workflow; add npm run dev script