Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Commands below assume you linked the binary and can run `igloo`. Swap in `igloo-
| `igloo about` | Summarize the FROSTR architecture and sibling projects. |
| `igloo status --share vault-share-1` | Decrypt a saved share and ping peers via the default relays. |
| `igloo signer --share vault-share-1 --password-file ./pass.txt` | Bring a decrypted share online as a signer until you quit. |
| `igloo relays` | Show effective and configured default relays. |
| `igloo relays set wss://a wss://b` | Set the default relays for status/signer. |
| `igloo policy --share vault-share-1` | Configure default send/receive rules and peer overrides for a share (alias of `igloo share policy`). |
| `igloo keyset create --name team --threshold 2 --total 3` | Interactive or flag-driven flow to generate, encrypt, and save shares. |
| `igloo keys convert --from nsec --value nsec1example...` | Convert between npub/nsec/hex formats using `@frostr/igloo-core`. |
Expand Down Expand Up @@ -106,6 +108,7 @@ Keyset commands and the signer flow support non-interactive execution:
- `--output ./directory` — change where encrypted share JSON is written.
- `--share id` — target a saved share by id/name when loading, diagnosing, or running the signer.
- `--relays wss://relay1,wss://relay2` — override the relay list for status checks and the signer.
Configure persistent defaults via `igloo relays set …`.
- `--verbose` — stream signer diagnostics (toggleable at runtime with the `l` key).
- `--log-level level` — pick signer log verbosity (`debug`, `info`, `warn`, or `error`).

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Command-line companion for the FROSTR ecosystem.",
"main": "dist/cli.js",
"scripts": {
"test": "npm run typecheck",
"test": "npm run typecheck && tsx --test tests/relays.test.ts",
"build": "tsup",
"dev": "tsx src/cli.tsx",
"start": "node dist/cli.js",
Expand Down
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {ShareLoad} from './components/share/ShareLoad.js';
import {ShareStatus} from './components/share/ShareStatus.js';
import {ShareSigner} from './components/share/ShareSigner.js';
import {ShareAdd} from './components/share/ShareAdd.js';
import {Relays} from './components/relays/Relays.js';

type AppProps = {
command: string;
Expand Down Expand Up @@ -131,6 +132,8 @@ export function App({command, args, flags, version}: AppProps) {
return <Setup threshold={threshold} total={total} />;
case 'about':
return <About />;
case 'relays':
return <Relays flags={flags} args={args} />;
case 'status':
return <ShareStatus flags={flags} args={args} />;
case 'signer':
Expand Down
14 changes: 13 additions & 1 deletion src/cli.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,19 @@ if (shouldShowVersion) {
}

if (showHelp) {
showHelpScreen(packageJson.version);
const helpable = new Set(['share', 'keyset', 'keys', 'relays']);
if (helpable.has((command ?? '').toLowerCase())) {
render(
<App
command={command}
args={args}
flags={flags}
version={packageJson.version}
/>
);
} else {
showHelpScreen(packageJson.version);
}
} else {
render(
<App
Expand Down
66 changes: 33 additions & 33 deletions src/components/Help.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,42 @@ type HelpProps = {
export function Help({version}: HelpProps) {
return (
<Box flexDirection="column" paddingX={1}>
<Text color="cyanBright">igloo-cli v{version}</Text>
<Text>Usage: igloo-cli [command] [options]</Text>
<Box marginTop={1} flexDirection="column" alignItems="center">
<Text color="cyanBright">IGLOO CLI</Text>
<Text color="white">FROSTR remote signing toolkit</Text>
<Text color="gray">version {version}</Text>
</Box>

<Box marginTop={1} flexDirection="column">
<Text color="cyan">Core commands</Text>
<Text>- igloo-cli setup -- bootstrap a fresh keyset</Text>
<Text>- igloo-cli keyset -- create keysets and shares</Text>
<Text>- igloo-cli share -- manage saved shares</Text>
<Text>- igloo-cli signer -- bring a share online as a signer</Text>
<Text>- igloo-cli status -- check relay and peer reachability</Text>
<Text>- igloo-cli keys -- translate between npub/nsec/hex</Text>
<Text>- igloo-cli relays -- manage default relay endpoints</Text>
<Text>- igloo-cli about -- outline the FROSTR stack</Text>
</Box>

<Box marginTop={1} flexDirection="column">
<Text color="cyan">Commands</Text>
<Text>- intro (default) Show the animated welcome.</Text>
<Text>- setup Step through signer bootstrapping.</Text>
<Text>- about Outline the FROSTR stack.</Text>
<Text>- signer Decrypt a share and run it as a signer.</Text>
<Text>- status Check peer reachability with a saved share.</Text>
<Text>- policy Configure send/receive permissions per peer.</Text>
<Text>- keyset Manage keyset creation, saving, loading, status.</Text>
<Text>- keys Convert between npub/nsec/hex formats.</Text>
<Text color="cyan">See subcommands</Text>
<Text>- igloo-cli share → lists add | list | load | status | signer | policy</Text>
<Text>- igloo-cli keyset → lists create</Text>
<Text>- igloo-cli keys → lists convert (with flag variants)</Text>
<Text>- igloo-cli relays → lists set | add | remove | reset</Text>
</Box>

<Box marginTop={1} flexDirection="column">
<Text color="cyan">Options</Text>
<Text>-h, --help Print this message.</Text>
<Text>-v, --version Print the version.</Text>
<Text>--threshold n Override default share threshold.</Text>
<Text>--total n Override total number of shares.</Text>
<Text>--name value Provide a keyset name during creation.</Text>
<Text>--nsec value Provide secret material during creation.</Text>
<Text>--password value Use a password non-interactively.</Text>
<Text>--password-file Read password from file.</Text>
<Text>--output path Save encrypted shares to a custom directory.</Text>
<Text>--share value Identify which saved share to load/status.</Text>
<Text>--relays list Override relay list (comma-separated).</Text>
<Text>--verbose Stream signer diagnostics to the console.</Text>
<Text>--log-level val Set signer log verbosity (debug|info|warn|error).</Text>
<Text>--from type Specify input type for keys convert (npub|nsec|hex-public|hex-private).</Text>
<Text>--value key Provide the key value for conversion.</Text>
<Text>--npub key Convert from an npub value.</Text>
<Text>--nsec key Convert from an nsec value.</Text>
<Text>--hex-public key Convert from a public hex key.</Text>
<Text>--hex-private key Convert from a private hex key.</Text>
<Text>--hex key Generic hex input (requires --kind public|private).</Text>
<Text>--kind type Pair with --hex to set the kind (public|private).</Text>
<Text color="cyan">Common options</Text>
<Text>-h, --help Show this help</Text>
<Text>-v, --version Print version</Text>
<Text>--share value Target a saved share by id/name</Text>
<Text>--password value Supply password non-interactively</Text>
<Text>--password-file Read password from file</Text>
<Text>--relays list Override relay list (comma-separated)</Text>
<Text>--verbose Stream signer diagnostics</Text>
<Text>--log-level level Signer log level (debug|info|warn|error)</Text>
</Box>
</Box>
);
Expand Down
16 changes: 8 additions & 8 deletions src/components/Intro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ export function Intro({version, commandExamples}: IntroProps) {

<Box marginTop={1} flexDirection="column">
<Text color="cyan">Core commands</Text>
<Text>- igloo-cli setup -- bootstrap a fresh keyset</Text>
<Text>- igloo-cli keyset create -- generate & encrypt shares headlessly</Text>
<Text>- igloo-cli share add -- import a share using its group</Text>
<Text>- igloo-cli share list -- review saved shares on this device</Text>
<Text>- igloo-cli share status -- check relay and peer reachability</Text>
<Text>- igloo-cli share policy -- tune defaults and peer overrides</Text>
<Text>- igloo-cli keys convert -- translate between npub/nsec/hex</Text>
<Text>- igloo-cli signer -- bring a share online as a signer</Text>
<Text>- igloo-cli setup -- bootstrap a fresh keyset</Text>
<Text>- igloo-cli keyset -- create keysets and shares</Text>
<Text>- igloo-cli share -- manage saved shares</Text>
<Text>- igloo-cli signer -- bring a share online as a signer</Text>
<Text>- igloo-cli status -- check relay and peer reachability</Text>
<Text>- igloo-cli keys -- translate between npub/nsec/hex</Text>
<Text>- igloo-cli relays -- manage default relay endpoints</Text>
<Text>- igloo-cli about -- outline the FROSTR stack</Text>
</Box>

<Box marginTop={1} flexDirection="column">
Expand Down
5 changes: 3 additions & 2 deletions src/components/keyset/KeysetSigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import fs from 'node:fs/promises';
import {
createAndConnectNode,
cleanupBifrostNode,
DEFAULT_PING_RELAYS,
decodeGroup,
decodeShare,
extractSelfPubkeyFromCredentials,
Expand All @@ -25,6 +24,7 @@ import {
decryptShareCredential,
ShareMetadata
} from '../../keyset/index.js';
import {resolveRelaysWithFallbackSync, DEFAULT_SIGNER_RELAYS} from '../../keyset/relays.js';

export type KeysetSignerProps = {
args: string[];
Expand Down Expand Up @@ -65,6 +65,7 @@ type LogEntry = {

const LOG_LEVELS: LogLevel[] = ['debug', 'info', 'warn', 'error'];


function parseBooleanFlag(value: string | boolean | undefined): boolean {
if (typeof value === 'boolean') {
return value;
Expand Down Expand Up @@ -248,7 +249,7 @@ export function KeysetSigner({args, flags}: KeysetSignerProps) {

const shareToken = typeof flags.share === 'string' ? flags.share : args[0];
const relayOverrides = parseRelayFlags(flags);
const relays = relayOverrides && relayOverrides.length > 0 ? relayOverrides : DEFAULT_PING_RELAYS;
const relays = resolveRelaysWithFallbackSync(relayOverrides, DEFAULT_SIGNER_RELAYS);

const nodeRef = useRef<BifrostNode | null>(null);
const peerManagerRef = useRef<PeerManager | null>(null);
Expand Down
3 changes: 2 additions & 1 deletion src/components/keyset/KeysetStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import {convert_pubkey} from '@frostr/bifrost/util';
import {readShareFiles, decryptShareCredential, ShareMetadata} from '../../keyset/index.js';
import {Prompt} from '../ui/Prompt.js';
import {resolveRelaysWithFallbackSync} from '../../keyset/relays.js';

export type KeysetStatusProps = {
flags: Record<string, string | boolean>;
Expand Down Expand Up @@ -272,7 +273,7 @@ export function KeysetStatus({flags, args}: KeysetStatusProps) {

const shareToken = typeof flags.share === 'string' ? flags.share : args[0];
const relayOverrides = parseRelayFlags(flags);
const relays = relayOverrides && relayOverrides.length > 0 ? relayOverrides : DEFAULT_PING_RELAYS;
const relays = resolveRelaysWithFallbackSync(relayOverrides, DEFAULT_PING_RELAYS);

useEffect(() => {
void (async () => {
Expand Down
16 changes: 13 additions & 3 deletions src/components/keyset/useShareEchoListener.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {awaitShareEcho, decodeGroup, createAndConnectNode, cleanupBifrostNode} from '@frostr/igloo-core';
import {
awaitShareEcho,
decodeGroup,
createAndConnectNode,
cleanupBifrostNode,
DEFAULT_ECHO_RELAYS
} from '@frostr/igloo-core';
import {resolveRelaysWithFallbackSync} from '../../keyset/relays.js';

export type EchoStatus = 'idle' | 'listening' | 'success';

Expand Down Expand Up @@ -76,11 +83,14 @@ export function useShareEchoListener(
}
try {
const decoded = decodeGroup(groupCredential);
return extractRelays(decoded);
const fromGroup = extractRelays(decoded);
if (fromGroup && fromGroup.length > 0) return fromGroup;
// fall back to configured relays, or DEFAULT_ECHO_RELAYS
return resolveRelaysWithFallbackSync(undefined, DEFAULT_ECHO_RELAYS);
} catch {
// ignore decode failures; we'll fall back to default relays
}
return undefined;
return resolveRelaysWithFallbackSync(undefined, DEFAULT_ECHO_RELAYS);
}, [groupCredential]);

const clearPending = useCallback(() => {
Expand Down
Loading