This commit is contained in:
Raven Scott
2026-04-22 05:40:16 -04:00
parent 33e343a127
commit 200873ab41
9 changed files with 196 additions and 276 deletions
+1 -19
View File
@@ -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')
}
+22 -9
View File
@@ -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
}
+3 -18
View File
@@ -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) {
+12 -19
View File
@@ -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) => {
+19 -8
View File
@@ -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]
)