@@ -38,7 +38,7 @@ Holesail Browser is composed of three parts: a browser extension, a native host
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ (managers/: certificate-authority, backup-manager, │
|
||||
│ ssh-manager, rdp-manager — see Source files below) │
|
||||
│ sync-manager, ssh-manager, rdp-manager — see Source files below)│
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Holesail P2P (Noise protocol, DHT)
|
||||
@@ -81,6 +81,7 @@ Holesail Browser is composed of three parts: a browser extension, a native host
|
||||
|------|---------|
|
||||
| `certificate-authority.js` | Generates a 2048-bit RSA root CA (10-year validity) using `node-forge`. Signs per-TLD wildcard domain certs on demand (1-year). Installs the CA into the OS trust store. Fingerprint-verifies to detect stale entries. |
|
||||
| `backup-manager.js` | Creates/restores `tar.gz` backups of `state.json` + all certificates. Supports create, list, restore, delete, and auto-prune by retention count. |
|
||||
| `sync-manager.js` | Syncs state (excluding certs) across linked devices via autopass. Handles `getSyncStatus`, `createSyncInvite`, `pairWithInvite`; pushes state on save, pulls on autopass `update` and applies via the same flow as backup restore. |
|
||||
| `ssh-manager.js` | Per SSH session: starts a Holesail client tunnel, spawns `ssh` with a real PTY via `tt-native` (`forkpty`), starts a `bare-ws` WebSocket server, and bridges PTY ↔ WebSocket. SSH is spawned only after the browser WebSocket client connects and sends a ready-signal. Password delivery uses `SSH_ASKPASS` + a named FIFO — public key auth is tried first; a password prompt appears in xterm.js only if key auth fails and no password is saved. |
|
||||
| `rdp-manager.js` | Per RDP/VNC session: starts a Holesail client tunnel, starts a WebSocket server. VNC: transparent byte pipe. RDP: `node-rdpjs-2` client, converts bitmap updates to JSON. |
|
||||
|
||||
@@ -95,7 +96,7 @@ Holesail Browser is composed of three parts: a browser extension, a native host
|
||||
| `servers.js` | `startServer()`, `stopServer()`, `getServers()`. Manages Holesail server tunnels (server mode — exposes a local port to the P2P network). |
|
||||
| `virtual-hosts.js` | `setVirtualHost()`, `removeVirtualHost()`, `getVirtualHosts()`, `getLocalBackend()`, `getLocalPortForHostname()`, `getVirtualHostMap()`. Manages virtual host client tunnels routed through the HTTPS proxy. |
|
||||
| `service-tunnels.js` | `startServiceTunnel()`, `stopServiceTunnel()`, `getServiceTunnels()`. Manages direct TCP client tunnels (not HTTP-proxied). |
|
||||
| `index.js` | Assembles all sub-modules, injects shared `saveState` and `emit` callbacks, exposes `restorePersistedState()` and `cleanup()`, and re-exports the complete holesail-manager API. |
|
||||
| `index.js` | Assembles all sub-modules, injects shared `saveState` and `emit` callbacks, exposes `restorePersistedState()` and `cleanup()`, and re-exports the complete holesail-manager API. Exposes `setOnStateSaved(callback)` used by sync-manager to push state after each save. |
|
||||
|
||||
### Extension (`extension/`)
|
||||
|
||||
@@ -166,6 +167,7 @@ Holesail Browser is composed of three parts: a browser extension, a native host
|
||||
| `service-tunnels.js` | `updateServiceTunnelsTable()`, `setupServiceTunnelEvents()` |
|
||||
| `proxy-ca.js` | `updateTabsTable()`, cert validator, `setupCertValidator()`, CA install logic |
|
||||
| `backups.js` | `updateBackupsTable()`, `refreshBackups()`, `setupBackupEvents()` |
|
||||
| `sync.js` | Sync page: device linking, create/pair invite, linked devices table |
|
||||
| `settings.js` | `updateSettingsUI()`, `saveSettings()` |
|
||||
| `ssh.js` | SSH state, `renderSshGrid()`, `connectSsh()`, xterm.js lifecycle, `setupSshEvents()` |
|
||||
| `rdp.js` | RDP/VNC state, `renderRdpGrid()`, `initVncViewer()`, `initRdpViewer()`, `setupRdpEvents()` |
|
||||
@@ -311,7 +313,9 @@ All state is owned by the native host and persisted to `state.json` next to the
|
||||
│ └── cert.pem
|
||||
└── holesail-browser-storage/
|
||||
├── state.json # all persistent state
|
||||
└── backups/ # tar.gz backup archives
|
||||
├── backups/ # tar.gz backup archives
|
||||
├── autopass/ # Corestore/autopass data for sync (when linked)
|
||||
└── autopass-identity.json # key material for the sync group (when linked)
|
||||
```
|
||||
|
||||
`state.json` schema (version 2):
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ Each backup archive contains:
|
||||
| `certs/wildcard.hole.sail/` | Wildcard cert for the default `.hole.sail` TLD |
|
||||
| `certs/wildcard.<parent>/` | One directory per custom TLD parent (e.g. `wildcard.my.internal/`, `wildcard.haha.wooo/`) |
|
||||
|
||||
The entire `holesail-browser-certs/` directory is archived — all wildcard cert directories are included, not just the default one. Backups do **not** include the binary itself, the log file, or other backup archives.
|
||||
The entire `holesail-browser-certs/` directory is archived — all wildcard cert directories are included, not just the default one. Backups do **not** include the binary itself, the log file, or other backup archives. To sync state across devices without copying certificates, see [Device sync](SYNC.md).
|
||||
|
||||
The default backup retention is **5 backups** (configurable in Settings via `backupRetention`).
|
||||
|
||||
|
||||
+10
-2
@@ -513,7 +513,7 @@ Delete a backup.
|
||||
|
||||
### `getSyncStatus`
|
||||
|
||||
Return whether this device is linked for sync and optional invite/last-synced info.
|
||||
Return whether this device is linked for sync, optional invite/last-synced info, device and sync-group IDs, and the list of linked devices (this device plus other peers in the sync group).
|
||||
|
||||
**Request payload:** `{}`
|
||||
|
||||
@@ -523,10 +523,18 @@ Return whether this device is linked for sync and optional invite/last-synced in
|
||||
"ok": true,
|
||||
"linked": true,
|
||||
"invite": "optional invite string if one was created",
|
||||
"lastSyncedAt": 1710000000000
|
||||
"lastSyncedAt": 1710000000000,
|
||||
"deviceId": "a1b2c3d4e5f6",
|
||||
"syncGroupId": "f6e5d4c3b2a1",
|
||||
"linkedDevices": [
|
||||
{ "id": "a1b2c3d4e5f6", "isCurrent": true },
|
||||
{ "id": "b2c3d4e5f678", "isCurrent": false }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When linked, `deviceId` is a short hex ID for this device’s writer key and `syncGroupId` is a short hex ID for the shared discovery key. `linkedDevices` is populated from the autopass active writers; it may contain only this device until other peers have replicated at least once.
|
||||
|
||||
---
|
||||
|
||||
### `createSyncInvite`
|
||||
|
||||
@@ -166,6 +166,10 @@ Protect `~/.holesail-browser/` accordingly.
|
||||
|
||||
Backup archives contain `state.json` and all certificate files including the CA private key. Store backups securely. The archives are not encrypted.
|
||||
|
||||
### Sync identity
|
||||
|
||||
The sync feature stores key material in `autopass-identity.json` under `holesail-browser-storage/`. Anyone with this file can access the synced state. Synced state includes base64-encoded SSH/RDP passwords. Do not share the identity file or the `autopass/` directory with untrusted parties; treat linked devices as trusted.
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
@@ -13,6 +13,24 @@ Holesail Browser can sync its state across two devices using [autopass](https://
|
||||
2. **Second device:** Open the dashboard → **Sync**. Paste the invite into the text field and click **Link device**. Wait for “Device linked. State has been synced.”
|
||||
3. Both devices are now linked. The second device’s state is replaced by the first device’s state at link time; thereafter changes on either device sync to the other.
|
||||
|
||||
**Replace-state warning:** When you click **Link device**, a confirmation modal appears. It explains that linking will replace all current state on this device (virtual hosts, tunnels, SSH/RDP connections, settings) with the state from the other device, and suggests creating a backup first if you want to keep a copy. Click **Replace state and link** to confirm, or **Cancel** to abort.
|
||||
|
||||
## Linked devices table
|
||||
|
||||
When linked, the Sync page shows a **Linked devices** table:
|
||||
|
||||
- **This device** — This machine’s device ID (a short hex fingerprint of its autopass writer key).
|
||||
- **Device 2**, **Device 3**, … — Other devices in the same sync group, each with their own ID. Other peers appear in the table after they have replicated at least once; if the other device is offline or has not yet synced, you may only see “This device” until replication runs.
|
||||
- **Sync group** — A shared ID (fingerprint of the discovery key) that is the same on every device in the group. You can confirm both devices are in the same group by checking that the Sync group ID matches on each.
|
||||
|
||||
## More than two devices
|
||||
|
||||
You can link more than two devices to the same sync group. From any device that is already linked, open the dashboard → **Sync** → **Create invite**. On the new device, paste the invite and click **Link device**. Each invite links one additional device; repeat as needed. All linked devices stay in sync with each other.
|
||||
|
||||
## Offline and back online
|
||||
|
||||
When a linked device goes offline (e.g. browser closed, machine asleep), it remains linked: the identity is stored in `autopass-identity.json`. When the device comes back and the native host starts again (e.g. you open the browser and the extension connects), it reopens the same sync group and will replicate with other peers when the network connects them. Any changes made on other devices while this one was offline will sync in once both are online.
|
||||
|
||||
## Behaviour after linking
|
||||
|
||||
- **Push:** Any change on a device (add/remove virtual host, edit settings, etc.) is saved to disk and pushed to the sync group. Other linked devices receive the update and apply it (tunnels are restarted as needed).
|
||||
@@ -33,3 +51,7 @@ Do not share or back up `autopass-identity.json` to an untrusted location; it al
|
||||
## Unlinking
|
||||
|
||||
To stop syncing, remove or rename the `autopass` directory and `autopass-identity.json` under `holesail-browser-storage`. The other device remains linked until it is restarted or its identity is removed; it will no longer receive updates from this device.
|
||||
|
||||
## Backups and sync
|
||||
|
||||
Backups (Dashboard → Backups) do not include the sync identity (`autopass-identity.json`) or the `autopass/` directory. Use backups for local snapshots (state + certificates); use sync for keeping state in sync across devices (state only, no certs).
|
||||
|
||||
Reference in New Issue
Block a user