Files
spark-control/package/startos/actions/showPublicKey.ts
T
Keysat 9ef9226e0a docs: split CLAUDE.md into path-scoped .claude/rules; fix dev/test commands
- CLAUDE.md trimmed to whole-repo facts (58 lines); subsystem guidance
  moved to .claude/rules/{startos-package,fastapi-image,redaction,
  audio-speech}.md with paths: frontmatter so each loads only when
  matching files are touched
- .gitignore: track .claude/rules/ while keeping the rest of .claude/
  (settings.local.json) ignored
- test-audio-with-speakers.sh: require audio-file arg in docs, replace
  owner-specific SPARK_CONTROL/VLLM defaults with generic ones
  (localhost dev server + Spark Control vLLM proxy), discover the
  loaded LLM via /api/status since /v1/models lists audio models only
- document REDACTION_MAP_DB + CONNECTIVITY_LOG as required for local
  dev (/data only exists in the container)
- prettier pass over startos/actions (formatting drift)
2026-06-11 19:12:23 -05:00

106 lines
3.7 KiB
TypeScript

import { sdk } from '../sdk'
import { promises as fs } from 'fs'
import * as path from 'path'
import { sparkConfigYaml } from '../fileModels/sparkConfig.yaml'
export const showPublicKey = sdk.Action.withoutInput(
'show-public-key',
async () => ({
name: 'Show Public Key',
description:
'Display the SSH public key and a ready-to-paste install command for granting this package SSH access to your Sparks.',
warning: null,
visibility: 'enabled',
allowedStatuses: 'any',
group: null,
}),
async ({ effects }) => {
// The container generates the key under /data/ssh/id_ed25519.pub on first boot.
const pubKeyPath = path.join(sdk.volumes.main.path, 'ssh', 'id_ed25519.pub')
let key: string
try {
key = (await fs.readFile(pubKeyPath, 'utf8')).trim()
} catch (e) {
return {
version: '1' as const,
title: 'Public Key Not Ready',
message:
'The service has not generated its SSH keypair yet. Start the service from the dashboard, wait a few seconds, then run this action again.',
result: null,
}
}
// If hosts are configured, construct a ready-to-paste one-liner.
const cfg = await sparkConfigYaml.read().once()
const hosts: Array<{ user: string; host: string }> = []
if (cfg) {
if (cfg.spark1_host && cfg.spark1_user)
hosts.push({ user: cfg.spark1_user, host: cfg.spark1_host })
if (cfg.spark2_host && cfg.spark2_user)
hosts.push({ user: cfg.spark2_user, host: cfg.spark2_host })
}
if (hosts.length === 0) {
// Not yet configured — show just the raw key with instructions to come back.
return {
version: '1' as const,
title: 'SSH Public Key',
message:
'Run the "Configure Sparks" action first to enter your Spark hostnames. Once that\'s saved, re-run this action and it will produce a ready-to-paste install command for granting access.',
result: {
type: 'single' as const,
name: 'Public Key',
description:
'The bare ed25519 public key line. You can also wait until Configure Sparks is filled in to get a complete install command.',
value: key,
masked: false,
copyable: true,
qr: false,
},
}
}
const sshLines = hosts
.map(
(h) =>
`ssh ${h.user}@${h.host} "mkdir -p ~/.ssh && echo '$KEY' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"`,
)
.join('\n')
const oneLiner = `KEY='${key}'\n${sshLines}`
return {
version: '1' as const,
title: 'SSH Public Key',
message:
'Run the command below on any machine that already has SSH access to your Sparks (typically your laptop). You will be prompted for the SSH password of each Spark once. After it completes, open the Web Interface to verify.',
result: {
type: 'group' as const,
name: 'Install command and raw key',
description: null,
value: [
{
type: 'single' as const,
name: 'Install command',
description:
'Copy this entire block and paste it into a terminal on your laptop. It will append the key to ~/.ssh/authorized_keys on each Spark.',
value: oneLiner,
masked: false,
copyable: true,
qr: false,
},
{
type: 'single' as const,
name: 'Raw public key',
description:
'Just the public key on its own, in case you prefer to install it manually.',
value: key,
masked: false,
copyable: true,
qr: false,
},
],
},
}
},
)