P1 spine: monorepo, shared WS protocol, server daemon
- npm workspaces (shared / server / web), TS strict, project refs - @arboretum/shared: multiplexed WS protocol (JSON control + binary output frames: 1B type + u32le channel), flow-control constants (ACK 256K, HIGH 384K, LOW 128K, lagging 2M), REST types - git-arboretum server: Fastify 5 + node:sqlite (single native dep: node-pty prebuilt), token auth (sha256 at rest, HMAC cookie, global login rate limit + backoff), strict Origin check on /api and /ws, PtyManager (2MiB ring with monotonic offset, resync replay = reset + 256KiB tail, pause/resume only when ALL interactive clients exceed HIGH, observers never throttle, lagging clients resync), WS gateway (attach/stdin/resize/ack, heartbeat 30s), SIGTERM→SIGKILL 5s grace - CLI: arboretum [--port 7317] [--bind 127.0.0.1] — non-loopback bind requires an explicit safety flag - Smoke-tested: login/401/403-origin/spawn bash/kill/grace-SIGKILL all green
This commit is contained in:
50
packages/server/src/config.ts
Normal file
50
packages/server/src/config.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { parseArgs } from 'node:util';
|
||||
import { join } from 'node:path';
|
||||
import { homedir } from 'node:os';
|
||||
import { mkdirSync } from 'node:fs';
|
||||
|
||||
export interface Config {
|
||||
port: number;
|
||||
bind: string;
|
||||
dbPath: string;
|
||||
dataDir: string;
|
||||
/** origins supplémentaires autorisées (ex. https://machine.tailnet.ts.net) */
|
||||
allowedOrigins: string[];
|
||||
printToken: boolean;
|
||||
}
|
||||
|
||||
export function loadConfig(argv = process.argv.slice(2)): Config {
|
||||
const { values } = parseArgs({
|
||||
args: argv,
|
||||
options: {
|
||||
port: { type: 'string', default: '7317' },
|
||||
bind: { type: 'string', default: '127.0.0.1' },
|
||||
db: { type: 'string' },
|
||||
'allow-origin': { type: 'string', multiple: true },
|
||||
'print-token': { type: 'boolean', default: false },
|
||||
'i-know-this-exposes-a-terminal': { type: 'boolean', default: false },
|
||||
},
|
||||
strict: true,
|
||||
});
|
||||
|
||||
const bind = values.bind ?? '127.0.0.1';
|
||||
const loopback = bind === '127.0.0.1' || bind === '::1' || bind === 'localhost';
|
||||
if (!loopback && !values['i-know-this-exposes-a-terminal']) {
|
||||
throw new Error(
|
||||
`Refusing to bind to ${bind}: an Arboretum server is remote code execution by design.\n` +
|
||||
`Use Tailscale Serve against the default 127.0.0.1 bind (recommended), or pass\n` +
|
||||
`--i-know-this-exposes-a-terminal if you really know what you are doing.`,
|
||||
);
|
||||
}
|
||||
|
||||
const dataDir = join(process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share'), 'arboretum');
|
||||
mkdirSync(dataDir, { recursive: true });
|
||||
return {
|
||||
port: Number(values.port),
|
||||
bind,
|
||||
dbPath: values.db ?? join(dataDir, 'arboretum.db'),
|
||||
dataDir,
|
||||
allowedOrigins: values['allow-origin'] ?? [],
|
||||
printToken: values['print-token'] ?? false,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user