simplify
This commit is contained in:
@@ -196,12 +196,7 @@ import {
|
||||
} from './lib/bare-os-theme-presets.js'
|
||||
import './lib/bare-cron.js'
|
||||
import { maybeStartBareHolesailKernelFromBooter } from './lib/bare-holesail.js'
|
||||
import {
|
||||
bareOsChatMuxEnabled,
|
||||
createBareOsChatService
|
||||
} from './lib/bare-os-chat-service.js'
|
||||
import { maybeRegisterBareOsChatInitd } from './lib/bare-os-chat-initd.js'
|
||||
import { maybeRegisterBareOsWwwInitd } from './lib/bare-os-www-initd.js'
|
||||
import { bareOsChatMuxEnabled } from './lib/bare-os-chat-service.js'
|
||||
import { runSshdCli, getBareOpensshProcJsonText } from './lib/bare-openssh.js'
|
||||
import { createHash } from 'bare-crypto'
|
||||
import { buildBareOsHostProcJsonText } from './lib/bare-os-host-proc-snapshot.js'
|
||||
@@ -9965,19 +9960,6 @@ async function main() {
|
||||
const swarm = new Hyperswarm(swarmOpts)
|
||||
bareOsRegisterCorestoreSuspendResumeHooks(store, swarm)
|
||||
const disk = new SwarmDisk()
|
||||
if (bareOsChatMuxEnabled(hostEnvMain)) {
|
||||
disk.bareOsChatService = createBareOsChatService({
|
||||
env: /** @type {Record<string, string | undefined>} */ (
|
||||
hostEnvMain || {}
|
||||
)
|
||||
})
|
||||
}
|
||||
maybeRegisterBareOsChatInitd(
|
||||
/** @type {Record<string, string | undefined>} */ (hostEnvMain || {})
|
||||
)
|
||||
maybeRegisterBareOsWwwInitd(
|
||||
/** @type {Record<string, string | undefined>} */ (hostEnvMain || {})
|
||||
)
|
||||
disk.swarmConnectionBudget = {
|
||||
schema: 1,
|
||||
requestedFromEnv: swarmOpts,
|
||||
|
||||
@@ -4,10 +4,8 @@ import { dirname } from '#host-path'
|
||||
* Multi-tunnel Holesail manager: persisted state on the VFS, one Holesail instance per entry.
|
||||
* Used when managed mode is on (stock default with BARE_OS_HOLESAIL_INITD/MANAGED).
|
||||
*
|
||||
* State path: **`BARE_OS_HOLESAIL_STATE`**, else **guest** → **`/.bare/holesail/guest/state.json`**
|
||||
* (boot / `USER=guest` / `HOME=/home/guest`), else **logged-in** → **`~/.holesail/state.json`** (writable under
|
||||
* `$HOME`). Stock **`bare-www-*`** rows use the **guest** file only in a guest session; after unlock they use the
|
||||
* same **`~/.holesail/state.json`** as other managed tunnels.
|
||||
* State path: **`BARE_OS_HOLESAIL_STATE`** override, else **`~/.holesail/state.json`** (resolves under **`$HOME`**).
|
||||
* Managed Holesail starts only after unlock; legacy **`/.bare/holesail/**`** files may be merged on first read.
|
||||
* Upstream holesail is AGPL-3.0.
|
||||
*/
|
||||
import { bareHolesailEnvTruthy } from './bare-holesail-env.js'
|
||||
@@ -17,7 +15,7 @@ import { appendVarLog, BARE_OS_VAR_LOG_DIR } from './bare-os-var-log.js'
|
||||
/** Per-user managed state (unlocked shells; resolves under `$HOME`). */
|
||||
export const BARE_HOLESAIL_STATE_USER = '~/.holesail/state.json'
|
||||
|
||||
/** Guest + initd stock tunnels (e.g. `bare-www-*`) on the personal drive. */
|
||||
/** Legacy guest-only managed state (migration source only). */
|
||||
export const BARE_HOLESAIL_STATE_GUEST = '/.bare/holesail/guest/state.json'
|
||||
|
||||
/** Pre-split single file at personal root (migration source only). */
|
||||
@@ -45,42 +43,14 @@ let managedServiceRunning = false
|
||||
export function bareHolesailManagedStatePath(env) {
|
||||
const raw = String(env.BARE_OS_HOLESAIL_STATE || '').trim()
|
||||
if (raw) return raw
|
||||
const user = String(env.USER || env.LOGNAME || '').trim()
|
||||
const home = String(env.HOME || '')
|
||||
.trim()
|
||||
.replace(/\/+$/, '')
|
||||
if (user === 'guest' || home === '/home/guest') return BARE_HOLESAIL_STATE_GUEST
|
||||
if (user && home.startsWith('/home/')) return BARE_HOLESAIL_STATE_USER
|
||||
/* Initd / early boot: stock tunnels use guest state. */
|
||||
return BARE_HOLESAIL_STATE_GUEST
|
||||
return BARE_HOLESAIL_STATE_USER
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest pre-login session uses {@link BARE_HOLESAIL_STATE_GUEST}; unlocked users use **`~/.holesail`**.
|
||||
* @param {Record<string, string | undefined>} env
|
||||
* @param {string} [_id]
|
||||
*/
|
||||
export function bareHolesailManagedSessionUsesGuestHolesailState(env) {
|
||||
const user = String(env.USER || env.LOGNAME || '').trim()
|
||||
const home = String(env.HOME || '')
|
||||
.trim()
|
||||
.replace(/\/+$/, '')
|
||||
return user === 'guest' || home === '/home/guest'
|
||||
}
|
||||
|
||||
/**
|
||||
* Stock **`bare-www-*`** rows use {@link BARE_HOLESAIL_STATE_GUEST} only while **`bare-os-www`** runs in a
|
||||
* guest session (initd boot). Unlocked shells use the same state file as {@link bareHolesailManagedStatePath}.
|
||||
* @param {Record<string, string | undefined>} env
|
||||
* @param {string} id
|
||||
*/
|
||||
export function bareHolesailManagedEnvForConnectionId(env, id) {
|
||||
const sid = String(id || '').trim()
|
||||
if (sid.startsWith('bare-www-') && bareHolesailManagedSessionUsesGuestHolesailState(env)) {
|
||||
return {
|
||||
.../** @type {Record<string, string | undefined>} */ (env),
|
||||
BARE_OS_HOLESAIL_STATE: BARE_HOLESAIL_STATE_GUEST
|
||||
}
|
||||
}
|
||||
export function bareHolesailManagedEnvForConnectionId(env, _id) {
|
||||
return env
|
||||
}
|
||||
|
||||
@@ -237,9 +207,8 @@ async function tryReadCoercedStateFile(ctx, vfs, logicalPath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* One-time upgrade when the primary state file is missing or empty: merge legacy sources.
|
||||
* - **Guest** primary: `/home/guest/.holesail/state.json` + old shared `/.bare/holesail/state.json`.
|
||||
* - **User** primary: old shared root only (avoids duplicating the target path).
|
||||
* One-time upgrade when the primary state file is missing or empty: merge legacy sources
|
||||
* (`/.bare/holesail/state.json`, old guest file, `/home/guest/.holesail/state.json`).
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {Record<string, string | undefined>} env
|
||||
* @param {string} primaryPath
|
||||
@@ -250,20 +219,13 @@ async function bareHolesailManagedTryMigrateLegacyState(ctx, env, primaryPath) {
|
||||
if (!vfs || typeof vfs.readFile !== 'function' || typeof vfs.resolveLogical !== 'function')
|
||||
return null
|
||||
|
||||
const normPrimary =
|
||||
String(primaryPath || '')
|
||||
.replace(/\/+$/, '')
|
||||
.replace(/\/+/g, '/') || '/'
|
||||
const normGuest = BARE_HOLESAIL_STATE_GUEST.replace(/\/+$/, '').replace(/\/+/g, '/')
|
||||
|
||||
void primaryPath
|
||||
/** @type {string[]} */
|
||||
const paths = []
|
||||
if (normPrimary === normGuest) {
|
||||
paths.push('/home/guest/.holesail/state.json')
|
||||
paths.push(BARE_HOLESAIL_STATE_LEGACY_ROOT)
|
||||
} else {
|
||||
paths.push(BARE_HOLESAIL_STATE_LEGACY_ROOT)
|
||||
}
|
||||
const paths = [
|
||||
BARE_HOLESAIL_STATE_LEGACY_ROOT,
|
||||
BARE_HOLESAIL_STATE_GUEST,
|
||||
'/home/guest/.holesail/state.json'
|
||||
]
|
||||
|
||||
/** @type {{ version: number, connections: Record<string, unknown> }} */
|
||||
let merged = { version: 1, connections: {} }
|
||||
@@ -337,10 +299,8 @@ export function bareHolesailManagedDebugEnabled(ctx, env) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When a managed **server** is live, persist `hs.info.url` into `state.json` as `key` when:
|
||||
* - the row has no `key` yet (first mint), or
|
||||
* - **guest** session only: disk `key` differs from the live URL (heal stale guest state).
|
||||
* Unlocked sessions never overwrite a non-empty persisted `key` (file is source of truth).
|
||||
* When a managed **server** is live, persist `hs.info.url` into `state.json` as `key` **only** when the row
|
||||
* has no `key` yet (first mint). Never replaces an existing persisted `key`.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {Record<string, string | undefined>} env
|
||||
* @param {string} id
|
||||
@@ -355,8 +315,7 @@ export async function bareHolesailManagedSyncPersistedServerKey(ctx, env, id) {
|
||||
const row = /** @type {Record<string, unknown>} */ (cur)
|
||||
if (!bareHolesailEnvTruthy(row.server)) return false
|
||||
const prev = String(row.key ?? '').trim()
|
||||
if (prev === url) return false
|
||||
if (prev && !bareHolesailManagedSessionUsesGuestHolesailState(env)) return false
|
||||
if (prev) return false
|
||||
row.key = url
|
||||
state.connections[id] = row
|
||||
await bareHolesailManagedWriteState(ctx, env, state)
|
||||
@@ -563,107 +522,3 @@ export async function bareHolesailManagedStopOne(ctx, id) {
|
||||
if (!idOk.ok) throw new Error(idOk.err)
|
||||
await stopManagedInstance(ctx, idOk.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop every managed **`bare-www-*`** tunnel (e.g. before guest **`bare-os-www`** restarts after logout).
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
export async function bareHolesailManagedStopAllBareWwwRuntime(ctx) {
|
||||
const ids = [...managedInstances.keys()].filter((id) => id.startsWith('bare-www-'))
|
||||
for (const id of ids) {
|
||||
await stopManagedInstance(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After unlock: move **`bare-www-*`** definitions from guest managed state into the unlocked user's
|
||||
* **`~/.holesail/state.json`**, restart tunnels, and **reuse** an existing user **`key`** when present so logins
|
||||
* do not rotate **`hs://`** URLs. For first-time adoption (no user key), uses the live runtime URL, then guest row.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
export async function bareHolesailManagedMigrateBareWwwFromGuestToUnlocked(ctx) {
|
||||
const env =
|
||||
ctx.env && typeof ctx.env === 'object'
|
||||
? /** @type {Record<string, string | undefined>} */ (ctx.env)
|
||||
: ctx.vfs?.env && typeof ctx.vfs.env === 'object'
|
||||
? /** @type {Record<string, string | undefined>} */ (ctx.vfs.env)
|
||||
: null
|
||||
if (!env) return
|
||||
if (bareHolesailManagedSessionUsesGuestHolesailState(env)) return
|
||||
if (!bareHolesailManagedServiceIsRunning()) return
|
||||
|
||||
const guestEnv = {
|
||||
.../** @type {Record<string, string | undefined>} */ (env),
|
||||
BARE_OS_HOLESAIL_STATE: BARE_HOLESAIL_STATE_GUEST
|
||||
}
|
||||
/** @type {Record<string, string | undefined>} */
|
||||
const userEnv = { ...env }
|
||||
delete userEnv.BARE_OS_HOLESAIL_STATE
|
||||
|
||||
const { state: guestState } = await bareHolesailManagedReadState(ctx, guestEnv)
|
||||
/** @type {Set<string>} */
|
||||
const ids = new Set()
|
||||
for (const id of Object.keys(guestState.connections)) {
|
||||
if (id.startsWith('bare-www-')) ids.add(id)
|
||||
}
|
||||
for (const id of managedInstances.keys()) {
|
||||
if (id.startsWith('bare-www-')) ids.add(id)
|
||||
}
|
||||
if (ids.size === 0) return
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const runtimeUrls = {}
|
||||
for (const id of ids) {
|
||||
const u = bareHolesailManagedRuntimeUrl(id).trim()
|
||||
if (u) runtimeUrls[id] = u
|
||||
}
|
||||
|
||||
for (const id of [...managedInstances.keys()]) {
|
||||
if (!id.startsWith('bare-www-')) continue
|
||||
await stopManagedInstance(ctx, id)
|
||||
}
|
||||
|
||||
const { state: userState } = await bareHolesailManagedReadState(ctx, userEnv)
|
||||
for (const id of ids) {
|
||||
const gRow = guestState.connections[id]
|
||||
const uRow = userState.connections[id]
|
||||
/** @type {Record<string, unknown>} */
|
||||
let row =
|
||||
uRow && typeof uRow === 'object'
|
||||
? { .../** @type {Record<string, unknown>} */ (uRow) }
|
||||
: gRow && typeof gRow === 'object'
|
||||
? { .../** @type {Record<string, unknown>} */ (gRow) }
|
||||
: null
|
||||
if (!row) continue
|
||||
const userKey =
|
||||
uRow && typeof uRow === 'object' ? String(uRow.key ?? '').trim() : ''
|
||||
const runtime = (runtimeUrls[id] || '').trim()
|
||||
const guestKey =
|
||||
gRow && typeof gRow === 'object' ? String(gRow.key ?? '').trim() : ''
|
||||
if (userKey) {
|
||||
row.key = userKey
|
||||
} else if (runtime) {
|
||||
row.key = runtime
|
||||
} else if (guestKey) {
|
||||
row.key = guestKey
|
||||
} else {
|
||||
delete row.key
|
||||
}
|
||||
userState.connections[id] = row
|
||||
}
|
||||
await bareHolesailManagedWriteState(ctx, userEnv, userState)
|
||||
|
||||
for (const id of ids) {
|
||||
const raw = userState.connections[id]
|
||||
if (!raw) continue
|
||||
const norm = bareHolesailManagedNormalizeEntry(raw)
|
||||
if (!norm.ok || !norm.entry.enabled) continue
|
||||
try {
|
||||
await bareHolesailManagedStartOne(ctx, userEnv, id)
|
||||
} catch (e) {
|
||||
const msg = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
|
||||
logLine(ctx, `migrate bare-www: start ${id} failed: ${msg}`)
|
||||
}
|
||||
}
|
||||
logLine(ctx, 'migrate: bare-www-* adopted into unlocked holesail state')
|
||||
}
|
||||
|
||||
@@ -32,6 +32,28 @@ let initdHookUnsubs = []
|
||||
/** @type {(() => void)[]} */
|
||||
let kernelHookUnsubs = []
|
||||
|
||||
/** @type {boolean} */
|
||||
let bareHolesailInitdRegistered = false
|
||||
|
||||
/**
|
||||
* Register the **bare-holesail** initd unit (call from **`bare-user-session-stack`** after unlock).
|
||||
* Stock **`BARE_OS_HOLESAIL_INITD`** default is on; set **`0`** to skip registration.
|
||||
* @param {Record<string, string | undefined>} env
|
||||
*/
|
||||
export function maybeRegisterBareHolesailInitd(env) {
|
||||
if (bareHolesailInitdRegistered) return
|
||||
if (!bareHolesailEnvTruthy(env.BARE_OS_HOLESAIL_INITD)) return
|
||||
bareHolesailInitdRegistered = true
|
||||
registerBareService({
|
||||
name: 'bare-holesail',
|
||||
description:
|
||||
'Holesail P2P TCP/UDP proxy (official holesail API); on by default (managed multi-tunnel for /bin/holesail); disable with BARE_OS_HOLESAIL_INITD=0 or systemctl disable',
|
||||
logPath: BARE_HOLESAIL_LOG,
|
||||
start: initdStartBareHolesail,
|
||||
stop: initdStopBareHolesail
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, string | undefined>} env
|
||||
* @param {string} key
|
||||
@@ -325,12 +347,3 @@ registerBareInitdDisposer(() => {
|
||||
initdHolesail = null
|
||||
void safeCloseHolesail(h)
|
||||
})
|
||||
|
||||
registerBareService({
|
||||
name: 'bare-holesail',
|
||||
description:
|
||||
'Holesail P2P TCP/UDP proxy (official holesail API); on by default (managed multi-tunnel for /bin/holesail); disable with BARE_OS_HOLESAIL_INITD=0 or systemctl disable',
|
||||
logPath: BARE_HOLESAIL_LOG,
|
||||
start: initdStartBareHolesail,
|
||||
stop: initdStopBareHolesail
|
||||
})
|
||||
|
||||
@@ -3,29 +3,12 @@
|
||||
*/
|
||||
import { bareHolesailEnvTruthy } from './bare-holesail-env.js'
|
||||
import {
|
||||
BARE_HOLESAIL_STATE_GUEST,
|
||||
bareHolesailManagedReadState,
|
||||
bareHolesailManagedServiceIsRunning,
|
||||
bareHolesailManagedSessionUsesGuestHolesailState,
|
||||
bareHolesailManagedStartOne,
|
||||
bareHolesailManagedWriteState
|
||||
} from './bare-holesail-managed.js'
|
||||
|
||||
/**
|
||||
* Guest session: persist **`bare-www-*`** next to initd boot state. Unlocked: caller's default
|
||||
* **`~/.holesail/state.json`** (same as other managed rows).
|
||||
* @param {Record<string, string | undefined>} env
|
||||
*/
|
||||
function wwwHolesailManagedEnv(env) {
|
||||
if (bareHolesailManagedSessionUsesGuestHolesailState(env)) {
|
||||
return {
|
||||
.../** @type {Record<string, string | undefined>} */ (env),
|
||||
BARE_OS_HOLESAIL_STATE: BARE_HOLESAIL_STATE_GUEST
|
||||
}
|
||||
}
|
||||
return /** @type {Record<string, string | undefined>} */ (env)
|
||||
}
|
||||
|
||||
export const BARE_OS_WWW_HOLESAIL_CONNECTION_ID_PREFIX = 'bare-www-'
|
||||
|
||||
/**
|
||||
@@ -64,8 +47,10 @@ export async function ensureBareOsWwwHolesailTunnel(ctx, env, port) {
|
||||
}
|
||||
if (!ctx.b4a || typeof ctx.b4a.from !== 'function') return
|
||||
|
||||
const gEnv = wwwHolesailManagedEnv(env)
|
||||
const { path, state } = await bareHolesailManagedReadState(ctx, gEnv)
|
||||
const { path, state } = await bareHolesailManagedReadState(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (env)
|
||||
)
|
||||
/** Holesail server mode requires an explicit loopback host. */
|
||||
const wantHost = '127.0.0.1'
|
||||
/** @type {Record<string, unknown>} */
|
||||
@@ -84,7 +69,11 @@ export async function ensureBareOsWwwHolesailTunnel(ctx, env, port) {
|
||||
if (server && curPort === p && enabled && hostOk) {
|
||||
if (bareHolesailManagedServiceIsRunning()) {
|
||||
try {
|
||||
await bareHolesailManagedStartOne(ctx, gEnv, id)
|
||||
await bareHolesailManagedStartOne(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (env),
|
||||
id
|
||||
)
|
||||
} catch {
|
||||
/* may already run */
|
||||
}
|
||||
@@ -100,11 +89,19 @@ export async function ensureBareOsWwwHolesailTunnel(ctx, env, port) {
|
||||
if (k) next.key = k
|
||||
}
|
||||
state.connections[id] = next
|
||||
await bareHolesailManagedWriteState(ctx, gEnv, state)
|
||||
await bareHolesailManagedWriteState(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (env),
|
||||
state
|
||||
)
|
||||
void path
|
||||
if (bareHolesailManagedServiceIsRunning()) {
|
||||
try {
|
||||
await bareHolesailManagedStartOne(ctx, gEnv, id)
|
||||
await bareHolesailManagedStartOne(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (env),
|
||||
id
|
||||
)
|
||||
} catch (e) {
|
||||
try {
|
||||
ctx.console?.error?.(
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* WWW + managed Holesail + swarm chat initd units — registered and started only after identity unlock,
|
||||
* stopped on logout. Guest boot skips this stack entirely.
|
||||
*/
|
||||
import {
|
||||
findBareServiceDefinition,
|
||||
startBareService,
|
||||
stopBareService
|
||||
} from './bare-initd.js'
|
||||
import { maybeRegisterBareHolesailInitd } from './bare-holesail.js'
|
||||
import { maybeRegisterBareOsChatInitd } from './bare-os-chat-initd.js'
|
||||
import { maybeRegisterBareOsWwwInitd } from './bare-os-www-initd.js'
|
||||
import {
|
||||
bareOsChatMuxEnabled,
|
||||
createBareOsChatService
|
||||
} from './bare-os-chat-service.js'
|
||||
|
||||
/** @param {Record<string, string | undefined>} env */
|
||||
export function registerBareUserSessionStackUnits(env) {
|
||||
maybeRegisterBareHolesailInitd(env)
|
||||
maybeRegisterBareOsWwwInitd(env)
|
||||
maybeRegisterBareOsChatInitd(env)
|
||||
}
|
||||
|
||||
/**
|
||||
* Idempotent: registers units once, attaches chat service to `ctx.disk` when mux is enabled, then starts
|
||||
* **bare-os-www** → **bare-holesail** → **bare-os-chat** (initd DAG order).
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
export async function startBareUserSessionStack(ctx) {
|
||||
const env =
|
||||
ctx.env && typeof ctx.env === 'object'
|
||||
? /** @type {Record<string, string | undefined>} */ (ctx.env)
|
||||
: /** @type {Record<string, string | undefined>} */ ({})
|
||||
|
||||
registerBareUserSessionStackUnits(env)
|
||||
|
||||
const disk = /** @type {{ bareOsChatService?: unknown }} */ (ctx.disk)
|
||||
if (disk && bareOsChatMuxEnabled(globalThis.process?.env) && !disk.bareOsChatService) {
|
||||
disk.bareOsChatService = createBareOsChatService({
|
||||
env: {
|
||||
.../** @type {Record<string, string | undefined>} */ (
|
||||
globalThis.process?.env || {}
|
||||
),
|
||||
...env
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const order = ['bare-os-www', 'bare-holesail', 'bare-os-chat']
|
||||
for (const name of order) {
|
||||
if (!findBareServiceDefinition(name)) continue
|
||||
try {
|
||||
await startBareService(ctx, name)
|
||||
} catch (e) {
|
||||
const msg = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
|
||||
try {
|
||||
ctx.console?.error?.(`[bare-user-stack] start ${name}: ${msg}`)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops **bare-holesail** (before www so the tunnel drops first), **bare-os-www**, **bare-os-chat**;
|
||||
* clears `disk.bareOsChatService` so guests do not pair chat on new peers.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
export async function stopBareUserSessionStack(ctx) {
|
||||
const order = ['bare-holesail', 'bare-os-www', 'bare-os-chat']
|
||||
for (const name of order) {
|
||||
if (!findBareServiceDefinition(name)) continue
|
||||
try {
|
||||
await stopBareService(ctx, name)
|
||||
} catch (e) {
|
||||
const msg = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
|
||||
try {
|
||||
ctx.console?.error?.(`[bare-user-stack] stop ${name}: ${msg}`)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
const disk = /** @type {{ bareOsChatService?: unknown }} */ (ctx.disk)
|
||||
if (disk) disk.bareOsChatService = undefined
|
||||
}
|
||||
@@ -1,18 +1,15 @@
|
||||
/**
|
||||
* Drive-resident /bin/holesail → ctx.bareOsRunHolesailCli (booter).
|
||||
* Manages persisted tunnels under BARE_OS_HOLESAIL_STATE (guest default `/.bare/holesail/guest/state.json`,
|
||||
* logged-in default `~/.holesail/state.json`; stock **`bare-www-*`** uses the guest file only before unlock).
|
||||
* Live start/stop uses the bare-holesail initd unit (managed mode is the stock default).
|
||||
* Manages persisted tunnels under **`BARE_OS_HOLESAIL_STATE`** or **`~/.holesail/state.json`** (per **`$HOME`**).
|
||||
* Live start/stop uses the **bare-holesail** initd unit after login (managed mode is the stock default).
|
||||
*/
|
||||
import {
|
||||
BARE_HOLESAIL_STATE_GUEST,
|
||||
bareHolesailManagedEnvForConnectionId,
|
||||
bareHolesailManagedNormalizeEntry,
|
||||
bareHolesailManagedReadState,
|
||||
bareHolesailManagedRuntimeRunning,
|
||||
bareHolesailManagedRuntimeUrl,
|
||||
bareHolesailManagedServiceIsRunning,
|
||||
bareHolesailManagedSessionUsesGuestHolesailState,
|
||||
bareHolesailManagedStartOne,
|
||||
bareHolesailManagedStatePath,
|
||||
bareHolesailManagedStopOne,
|
||||
@@ -35,19 +32,7 @@ function ctxEnv(ctx) {
|
||||
* @param {Record<string, string | undefined>} env
|
||||
*/
|
||||
async function holesailCliReadStateMergedForList(ctx, env) {
|
||||
let { path, state } = await bareHolesailManagedReadState(ctx, env)
|
||||
if (!bareHolesailManagedServiceIsRunning()) return { path, state }
|
||||
if (!bareHolesailManagedSessionUsesGuestHolesailState(env)) return { path, state }
|
||||
const envG = { ...env, BARE_OS_HOLESAIL_STATE: BARE_HOLESAIL_STATE_GUEST }
|
||||
const g = await bareHolesailManagedReadState(ctx, envG)
|
||||
if (path === g.path || Object.keys(g.state.connections).length === 0) return { path, state }
|
||||
return {
|
||||
path,
|
||||
state: {
|
||||
version: 1,
|
||||
connections: { ...g.state.connections, ...state.connections }
|
||||
}
|
||||
}
|
||||
return bareHolesailManagedReadState(ctx, env)
|
||||
}
|
||||
|
||||
function printHelp(ctx, prog) {
|
||||
|
||||
@@ -28,13 +28,10 @@ import {
|
||||
} from './sshd-config-parse.js'
|
||||
import { ensureBareOsVarLogTree } from './bare-os-var-log.js'
|
||||
import {
|
||||
bareHolesailManagedMigrateBareWwwFromGuestToUnlocked,
|
||||
bareHolesailManagedStopAllBareWwwRuntime
|
||||
} from './bare-holesail-managed.js'
|
||||
import {
|
||||
ensureBareOsWwwHomeDefaults,
|
||||
maybeRestartBareOsWwwAfterIdentity
|
||||
} from './bare-os-www-initd.js'
|
||||
startBareUserSessionStack,
|
||||
stopBareUserSessionStack
|
||||
} from './bare-user-session-stack.js'
|
||||
import { ensureBareOsWwwHomeDefaults } from './bare-os-www-initd.js'
|
||||
|
||||
const LEGACY_ROOT_MIGRATION_STATE = '/.bare-os/migration/legacy-root-v1.json'
|
||||
|
||||
@@ -387,13 +384,12 @@ export async function applyLoginKeys(ctx, keys) {
|
||||
ctx.console?.error?.('[bare-os] ensureBareOsWwwHomeDefaults: ' + (e?.message || e))
|
||||
}
|
||||
try {
|
||||
await bareHolesailManagedMigrateBareWwwFromGuestToUnlocked(ctx)
|
||||
await startBareUserSessionStack(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.(
|
||||
'[bare-os] holesail migrate before www restart: ' + (e?.message || e)
|
||||
'[bare-os] user session stack after login: ' + (e?.message || e)
|
||||
)
|
||||
}
|
||||
await maybeRestartBareOsWwwAfterIdentity(ctx)
|
||||
maybeAppendVaultMultisigContinuityAudit(ctx, 'applyLoginKeys')
|
||||
}
|
||||
|
||||
@@ -478,13 +474,12 @@ export async function registerIdentity(ctx, passphrase) {
|
||||
ctx.console?.error?.('[bare-os] ensureBareOsWwwHomeDefaults: ' + (e?.message || e))
|
||||
}
|
||||
try {
|
||||
await bareHolesailManagedMigrateBareWwwFromGuestToUnlocked(ctx)
|
||||
await startBareUserSessionStack(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.(
|
||||
'[bare-os] holesail migrate before www restart: ' + (e?.message || e)
|
||||
'[bare-os] user session stack after register: ' + (e?.message || e)
|
||||
)
|
||||
}
|
||||
await maybeRestartBareOsWwwAfterIdentity(ctx)
|
||||
try {
|
||||
await bareOsAppendVaultRotationCheckpoint(ctx, {
|
||||
kind: 'identity_register',
|
||||
@@ -534,13 +529,12 @@ export async function unlockIdentity(ctx, passphrase) {
|
||||
ctx.console?.error?.('[bare-os] ensureBareOsWwwHomeDefaults: ' + (e?.message || e))
|
||||
}
|
||||
try {
|
||||
await bareHolesailManagedMigrateBareWwwFromGuestToUnlocked(ctx)
|
||||
await startBareUserSessionStack(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.(
|
||||
'[bare-os] holesail migrate before www restart: ' + (e?.message || e)
|
||||
'[bare-os] user session stack after unlock: ' + (e?.message || e)
|
||||
)
|
||||
}
|
||||
await maybeRestartBareOsWwwAfterIdentity(ctx)
|
||||
try {
|
||||
await bareOsAppendVaultRotationCheckpoint(ctx, {
|
||||
kind: 'identity_unlock',
|
||||
@@ -585,15 +579,14 @@ export async function logoutIdentity(ctx, opts = {}) {
|
||||
/* ignore audit failures */
|
||||
}
|
||||
try {
|
||||
await bareHolesailManagedStopAllBareWwwRuntime(ctx)
|
||||
await stopBareUserSessionStack(ctx)
|
||||
} catch (e) {
|
||||
ctx.console?.error?.(
|
||||
'[bare-os] holesail stop bare-www before logout: ' + (e?.message || e)
|
||||
'[bare-os] user session stack stop before logout: ' + (e?.message || e)
|
||||
)
|
||||
}
|
||||
wipeSecret(ctx)
|
||||
await applyGuestEnv(ctx)
|
||||
await maybeRestartBareOsWwwAfterIdentity(ctx)
|
||||
ctx.console.log('Logged out (guest)')
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import test from 'brittle'
|
||||
import {
|
||||
BARE_HOLESAIL_STATE_GUEST,
|
||||
BARE_HOLESAIL_STATE_USER,
|
||||
bareHolesailManagedEnvForConnectionId,
|
||||
bareHolesailManagedNormalizeEntry,
|
||||
@@ -9,13 +8,13 @@ import {
|
||||
} from './lib/bare-holesail-managed.js'
|
||||
import { holesailCliParseAdd } from './lib/holesail-cli.js'
|
||||
|
||||
test('bareHolesailManagedStatePath guest vs logged-in vs override', (t) => {
|
||||
test('bareHolesailManagedStatePath defaults to ~/.holesail and honors override', (t) => {
|
||||
t.is(
|
||||
bareHolesailManagedStatePath({
|
||||
USER: 'guest',
|
||||
HOME: '/home/guest'
|
||||
}),
|
||||
BARE_HOLESAIL_STATE_GUEST
|
||||
BARE_HOLESAIL_STATE_USER
|
||||
)
|
||||
t.is(
|
||||
bareHolesailManagedStatePath({
|
||||
@@ -32,18 +31,15 @@ test('bareHolesailManagedStatePath guest vs logged-in vs override', (t) => {
|
||||
}),
|
||||
'/tmp/custom.json'
|
||||
)
|
||||
t.is(bareHolesailManagedStatePath({}), BARE_HOLESAIL_STATE_GUEST)
|
||||
t.is(bareHolesailManagedStatePath({}), BARE_HOLESAIL_STATE_USER)
|
||||
})
|
||||
|
||||
test('bareHolesailManagedEnvForConnectionId bare-www uses guest state only for guest', (t) => {
|
||||
const env = { USER: 'u', HOME: '/home/u' }
|
||||
test('bareHolesailManagedEnvForConnectionId is identity on env', (t) => {
|
||||
const env = { USER: 'u', HOME: '/home/u', BARE_OS_HOLESAIL_STATE: '/x.json' }
|
||||
const g = bareHolesailManagedEnvForConnectionId(env, 'bare-www-8088')
|
||||
t.ok(g.BARE_OS_HOLESAIL_STATE == null || g.BARE_OS_HOLESAIL_STATE === '')
|
||||
const guest = { USER: 'guest', HOME: '/home/guest' }
|
||||
const gw = bareHolesailManagedEnvForConnectionId(guest, 'bare-www-8088')
|
||||
t.is(gw.BARE_OS_HOLESAIL_STATE, BARE_HOLESAIL_STATE_GUEST)
|
||||
t.is(g, env)
|
||||
const u = bareHolesailManagedEnvForConnectionId(env, 'my-tunnel')
|
||||
t.ok(u.BARE_OS_HOLESAIL_STATE == null || u.BARE_OS_HOLESAIL_STATE === '')
|
||||
t.is(u, env)
|
||||
})
|
||||
|
||||
test('bareHolesailManagedValidateId', (t) => {
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
} from './lib/bare-os-www-initd.js'
|
||||
import { findBareServiceDefinition } from './lib/bare-initd.js'
|
||||
import {
|
||||
BARE_HOLESAIL_STATE_GUEST,
|
||||
bareHolesailManagedReadState,
|
||||
bareHolesailManagedWriteState
|
||||
} from './lib/bare-holesail-managed.js'
|
||||
@@ -172,11 +171,17 @@ test('ensureBareOsWwwHolesailTunnel persists managed server entry', async (t) =>
|
||||
})
|
||||
const p = 18188
|
||||
const id = bareOsWwwHolesailConnectionId(p)
|
||||
const guestEnv = { ...ctx.env, BARE_OS_HOLESAIL_STATE: BARE_HOLESAIL_STATE_GUEST }
|
||||
const empty = { version: 1, connections: {} }
|
||||
await bareHolesailManagedWriteState(ctx, guestEnv, empty)
|
||||
await bareHolesailManagedWriteState(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (ctx.env),
|
||||
empty
|
||||
)
|
||||
await ensureBareOsWwwHolesailTunnel(ctx, ctx.env, p)
|
||||
const { state } = await bareHolesailManagedReadState(ctx, guestEnv)
|
||||
const { state } = await bareHolesailManagedReadState(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (ctx.env)
|
||||
)
|
||||
const row = state.connections[id]
|
||||
t.ok(row && /** @type {{ server?: boolean }} */ (row).server === true)
|
||||
t.is(/** @type {{ port?: number }} */ (row).port, p)
|
||||
@@ -201,9 +206,11 @@ test('ensureBareOsWwwHolesailTunnel preserves existing hs key when normalizing r
|
||||
})
|
||||
const p = 18189
|
||||
const id = bareOsWwwHolesailConnectionId(p)
|
||||
const guestEnv = { ...ctx.env, BARE_OS_HOLESAIL_STATE: BARE_HOLESAIL_STATE_GUEST }
|
||||
const keepKey = 'hs://0000keepthiskey000000000000000000000000000000000000000000'
|
||||
await bareHolesailManagedWriteState(ctx, guestEnv, {
|
||||
await bareHolesailManagedWriteState(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (ctx.env),
|
||||
{
|
||||
version: 1,
|
||||
connections: {
|
||||
[id]: {
|
||||
@@ -214,9 +221,13 @@ test('ensureBareOsWwwHolesailTunnel preserves existing hs key when normalizing r
|
||||
key: keepKey
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
await ensureBareOsWwwHolesailTunnel(ctx, ctx.env, p)
|
||||
const { state } = await bareHolesailManagedReadState(ctx, guestEnv)
|
||||
const { state } = await bareHolesailManagedReadState(
|
||||
ctx,
|
||||
/** @type {Record<string, string | undefined>} */ (ctx.env)
|
||||
)
|
||||
const row = /** @type {{ port?: number, host?: string, key?: string }} */ (
|
||||
state.connections[id]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user