synfull 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,11 +13,11 @@ synfull start
13
13
  synfull open
14
14
  ```
15
15
 
16
- During interactive `synfull setup`, you can choose whether to bind to `0.0.0.0` for access from other devices on your LAN; localhost-only (`127.0.0.1`) remains the default, and non-interactive setup stays local-only. LAN binding exposes the login page and server to every reachable network interface: use a trusted LAN, configure the host firewall to allow only the devices/subnet you intend, change the initial `admin` / `admin` password locally before signing in remotely, and do not expose Synfull directly to the public internet. npm installation alone does not register a background service or change system startup.
16
+ During interactive `synfull setup`, you can choose whether to bind to `0.0.0.0` for access from other devices on your LAN; localhost-only (`127.0.0.1`) remains the default, and non-interactive setup stays local-only. On Linux, setup also offers to add your account to the `docker` group so the Docker integration can access the daemon. This is optional and grants root-equivalent control of the computer; decline if you do not fully trust the account with administrator-level Docker access. If Synfull's systemd service is installed, setup restarts it after group membership changes; otherwise, an interactive user session may need a logout/login before new group membership applies. LAN binding exposes the login page and server to every reachable network interface: use a trusted LAN, configure the host firewall to allow only intended devices/subnets, change the initial `admin` / `admin` password locally before signing in remotely, and do not expose Synfull directly to the public internet. npm installation alone does not register a background service or change system startup.
17
17
 
18
18
  ### Commands
19
19
 
20
- - `synfull setup` asks whether to allow LAN access, then prepares the isolated Python runtime and dependencies. The choice is saved in the CLI config and used by later starts and service registration.
20
+ - `synfull setup` asks whether to allow LAN access and, on Linux, whether to grant the current account Docker-group access; it then prepares the isolated Python runtime and dependencies. Both choices are optional and are used by later starts/service registration. Docker group membership is root-equivalent. If an installed Synfull systemd service is present, setup restarts it after adding group access; otherwise log out and back in for a shell session to receive the new group membership.
21
21
  - `synfull start` runs in the background; `synfull run` keeps the server in the foreground.
22
22
  - `synfull status`, `synfull stop`, and `synfull open` manage a manually started instance.
23
23
  - `synfull install-service` explicitly enables automatic start and restart. On Windows this registers a per-user logon scheduled task. On Linux it creates and enables a system-wide systemd service and therefore requires `sudo`; the service runs as your invoking user, not root.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synfull",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Local-first cross-platform host monitoring dashboard",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -66,16 +66,62 @@ function saveBindHost(host) {
66
66
  fs.mkdirSync(HOME, { recursive: true })
67
67
  fs.writeFileSync(CLI_CONFIG, `${JSON.stringify({ bindHost: host }, null, 2)}\n`, { mode: 0o600 })
68
68
  }
69
- async function configureBindHost() {
69
+ async function configureBindHost(rl) {
70
70
  if (!process.stdin.isTTY || !process.stdout.isTTY) return
71
+ const answer = await rl.question('Allow other devices on your LAN to access Synfull by binding to 0.0.0.0? (y/N): ')
72
+ const host = /^y(es)?$/i.test(answer.trim()) ? '0.0.0.0' : '127.0.0.1'
73
+ saveBindHost(host)
74
+ console.log(host === '0.0.0.0'
75
+ ? 'LAN access enabled. Change the initial admin password from this machine before signing in from another device; use a trusted LAN and configure your firewall.'
76
+ : 'Synfull will only accept connections from this machine.')
77
+ }
78
+ async function configureDockerAccess(rl, options = {}) {
79
+ const platform = options.platform || process.platform
80
+ const interactive = options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY)
81
+ const username = options.username || process.env.SUDO_USER || os.userInfo().username
82
+ const isRoot = options.isRoot ?? Boolean(process.getuid && process.getuid() === 0)
83
+ const spawnCommand = options.spawnSync || spawnSync
84
+ const runCommand = options.run || run
85
+ if (platform !== 'linux' || !interactive) return false
86
+ if (isRoot) {
87
+ console.log('Run `synfull setup` as your normal account (not sudo); setup will request sudo only if Docker group access is approved.')
88
+ return false
89
+ }
90
+ const dockerGroup = spawnCommand('getent', ['group', 'docker'], { encoding: 'utf8', windowsHide: true })
91
+ if (dockerGroup.status !== 0) {
92
+ console.log('No Linux docker group was found. Install/configure Docker Engine first; Synfull did not change account permissions.')
93
+ return false
94
+ }
95
+ console.log('Docker group membership grants root-equivalent control of this computer (including control over containers and host files).')
96
+ const answer = await rl.question(`Allow Synfull to use Docker by adding ${username} to the docker group? (y/N): `)
97
+ if (!/^y(es)?$/i.test(answer.trim())) {
98
+ console.log('Docker group access was not changed. The Docker integration will remain unavailable unless your account already has socket access.')
99
+ return false
100
+ }
101
+ const groups = spawnCommand('id', ['-nG', username], { encoding: 'utf8', windowsHide: true })
102
+ const alreadyMember = groups.status === 0 && groups.stdout.trim().split(/\s+/).includes('docker')
103
+ if (!alreadyMember) {
104
+ const command = process.getuid && process.getuid() === 0 ? 'usermod' : 'sudo'
105
+ const args = command === 'sudo' ? ['usermod', '-aG', 'docker', username] : ['-aG', 'docker', username]
106
+ runCommand(command, args)
107
+ console.log(`Added ${username} to the docker group.`)
108
+ } else {
109
+ console.log(`${username} is already in the docker group.`)
110
+ }
111
+ const serviceInstalled = spawnCommand('systemctl', ['cat', 'synfull-npm.service'], { stdio: 'ignore', windowsHide: true }).status === 0
112
+ if (!serviceInstalled) console.log('No Synfull systemd service is installed. Log out/in (or start a fresh session with Docker group membership) before running Synfull so it receives the new group permissions.')
113
+ return serviceInstalled
114
+ }
115
+ function restartSynfullService(runCommand = run) {
116
+ runCommand('sudo', ['systemctl', 'restart', 'synfull-npm.service'])
117
+ console.log('Restarted the Synfull system service so it picks up Docker group access.')
118
+ }
119
+ async function configureSetup() {
120
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return false
71
121
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
72
122
  try {
73
- const answer = await rl.question('Allow other devices on your LAN to access Synfull by binding to 0.0.0.0? (y/N): ')
74
- const host = /^y(es)?$/i.test(answer.trim()) ? '0.0.0.0' : '127.0.0.1'
75
- saveBindHost(host)
76
- console.log(host === '0.0.0.0'
77
- ? 'LAN access enabled. Change the initial admin password from this machine before signing in from another device; use a trusted LAN and configure your firewall.'
78
- : 'Synfull will only accept connections from this machine.')
123
+ await configureBindHost(rl)
124
+ return await configureDockerAccess(rl)
79
125
  } finally {
80
126
  rl.close()
81
127
  }
@@ -237,7 +283,12 @@ async function main() {
237
283
  if (command === '--version' || command === '-v') { console.log(VERSION); return }
238
284
  if (command === '--help' || command === '-h') { help(); return }
239
285
  if (!['win32', 'linux'].includes(process.platform)) throw new Error('Synfull npm CLI currently supports Windows and Linux.')
240
- if (command === 'setup') { await configureBindHost(); ensureRuntime(); console.log(`Runtime ready. Data directory: ${STATE_DIR}`) }
286
+ if (command === 'setup') {
287
+ const restartServiceForDocker = await configureSetup()
288
+ ensureRuntime()
289
+ if (restartServiceForDocker) restartSynfullService()
290
+ console.log(`Runtime ready. Data directory: ${STATE_DIR}`)
291
+ }
241
292
  else if (command === 'start') startServer(false)
242
293
  else if (command === 'run') startServer(true)
243
294
  else if (command === 'stop') stopServer()
@@ -253,4 +304,4 @@ async function main() {
253
304
  else help()
254
305
  }
255
306
  if (require.main === module) main().catch(error => { console.error(`Synfull: ${error.message}`); process.exitCode = 1 })
256
- module.exports = { ensureRuntime, serverEnvironment, configuredBindHost, selectedBindHost, saveBindHost, APP_DIR, STATE_DIR, ROOT }
307
+ module.exports = { ensureRuntime, serverEnvironment, configuredBindHost, selectedBindHost, saveBindHost, configureDockerAccess, restartSynfullService, APP_DIR, STATE_DIR, ROOT }