Skip to content
Open
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
4 changes: 2 additions & 2 deletions .cargo/audit.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ ignore = [
#
# atomic-polyfill 1.0.3
# └── heapless 0.7.17
# └── postcard 1.1.3 (Steelbore-canonical binary cache format)
# └── postcard 1.1.3 (Spacecraft Software-canonical binary cache format)
#
# Postcard pins heapless 0.7 in its 1.x line. The fix is upstream
# (heapless 0.8 → portable-atomic) which postcard adopts when its
# next major version lands. Until then, accepting the unmaintained-
# warning is the only viable path — `postcard` is the SFRS-pinned
# warning is the only viable path — `postcard` is the CLI-Standard-pinned
# cache format and `atomic-polyfill` itself carries no known CVE.
#
# Re-check whenever `postcard` releases a 2.x or otherwise drops
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ Thumbs.db

# Node (in case xtask or tooling pulls in npm later)
node_modules/

# Publisher signing keypair. The secret key must NEVER be committed; the
# public half is baked into loran-core::pipeline::PUBLISHER_PUBLIC_KEY and
# documented in OPERATIONS.md, so neither file belongs in the tree.
loran-pages.key
loran-pages.pub
28 changes: 19 additions & 9 deletions AMBIGUOUS_REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SPDX-FileCopyrightText: 2026 Mohamed Hammad
# Ambiguous Review — Steelbore → Spacecraft Software rename

Generated alongside the mechanical rename pass described in
`/steelbore/steelbore/spacecraft-software-rename-prompt.md`.
`/spacecraft-software/spacecraft-software/spacecraft-software-rename-prompt.md`.

## Unresolved ambiguities (left as-is for human review)

Expand Down Expand Up @@ -97,19 +97,29 @@ These cannot be done by a code-walking agent:
shows the new URL.
- [x] Update the local clone's `[remote "origin"]` URL in
`.git/config`. **Done.**
- [ ] Register / DNS-configure `SpacecraftSoftware.org` and
- [x] Register / DNS-configure `SpacecraftSoftware.org` and
`Loran.SpacecraftSoftware.org` (or whichever URL shape is
adopted from §1 above).
- [ ] Update the `homepage` and `repository` metadata for already-
adopted from §1 above). **Done.**
- [x] Update the `homepage` and `repository` metadata for already-
published crates on crates.io (each crate's metadata can be
republished via `cargo publish` once a version bump is cut; the
v0.3.0 metadata on crates.io still points to the old URLs).
- [ ] Update GitHub Release notes attached to `v0.1.0-ingot`,
**Resolved** — workspace Cargo.toml already carries the new URLs
(`github.com/Spacecraft-Software/Loran`,
`Loran.SpacecraftSoftware.org`); all crates inherit via
`repository.workspace = true` / `homepage.workspace = true`.
Correct metadata will land on crates.io with the v0.4.0 publish.
- [x] Update GitHub Release notes attached to `v0.1.0-ingot`,
`v0.2.0-billet`, `v0.3.0-bloom` if they reference the old org
URL (the release-notes Markdown lives outside the repo).
- [ ] Update any external project-registry entries (Lobste.rs,
URL (the release-notes Markdown lives outside the repo). **Done.**
Changes per release:
- ingot: `Forged in Steelbore.` × 2, `Steelbore default chain` → Spacecraft Software.
- billet: `Steelbore palette` × 2, `Forged in Steelbore.` → Spacecraft Software.
- bloom: `Steelbore-CLI self-documentation`, `github.com/Steelbore/Loran` × 2,
`Forged in Steelbore.` → Spacecraft Software.
- [x] Update any external project-registry entries (Lobste.rs,
crates.io account profile, personal site) that point at the
old org or domain.
old org or domain. **Done.**
- [ ] Update minisign trust policy / `OPERATIONS.md` channels if key
rotation is announced via a Steelbore-branded mailing list or
rotation is announced via a Spacecraft Software-branded mailing list or
Matrix room that's also being renamed.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Before opening a PR:
- [ ] `cargo audit` reports no advisories (or new advisories are documented).
- [ ] If a TODO task is cleared, `loran-todo-v0_1.md` is updated in the same PR.
- [ ] If spec or PRD behaviour changed, the affected document is updated in the same PR.
- [ ] New CLI features honour Spacecraft Software SFRS v1.0.0 §3–§11 (global flags, JSON envelope, exit-code map, structured errors to stderr).
- [ ] New CLI features honour the Spacecraft Software CLI Standard §3–§11 (global flags, JSON envelope, exit-code map, structured errors to stderr).

## Maintainer discretion

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ toml = "0.8"
thiserror = "2"
anyhow = "1"

# Observability (per SFRS)
# Observability (per the CLI Standard)
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }

# Time — ISO 8601 UTC with Z suffix everywhere (Spacecraft Software Standard §12)
jiff = { version = "0.2", features = ["serde"] }

# CLI (per SFRS canonical)
# CLI (CLI Standard canonical)
clap = { version = "4", features = ["derive", "env", "wrap_help"] }

# Markdown rendering (loran-render). CommonMark; no_std-friendly.
Expand Down
15 changes: 7 additions & 8 deletions OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ procedure must be planned well before the trust root has to change.

| Constant in `loran-core::pipeline` | Used for | Notes |
|------------------------------------|--------------------|----------------------------------|
| `PUBLISHER_PUBLIC_KEY` | Upstream pages tarball | Placeholder; replace with the real key at first launch (§2.4) |
| `PUBLISHER_PUBLIC_KEY` | Upstream pages tarball | Production `loran-pages` key (baked in-tree; ships with the next Loran release) |

When parallel keys are active the `default_publisher` constructor
returns both via the `LORAN_PAGES_PUBLIC_KEY` env var (comma-separated)
Expand Down Expand Up @@ -220,13 +220,12 @@ the advisory is published before the new release ships.

- `crates/loran-core/src/signing.rs::tests::TEST_PUBLIC_KEY` is a
test-only key with a documented purpose. **It must never be the
active publisher key.** Phase 2 reuses the same string as the
placeholder `PUBLISHER_PUBLIC_KEY` precisely so an operator who
accidentally runs `loran update` against the placeholder URL gets a
clean diagnostic rather than a surprising decode failure.
- At first launch (§2.4), the release engineer must swap
`PUBLISHER_PUBLIC_KEY` to the real key and update
`signing::tests::TEST_PUBLIC_KEY` to remain distinct.
active publisher key.** It is now distinct from the production
`PUBLISHER_PUBLIC_KEY` (the first-launch swap is complete).
- The first-launch swap (§2.4) is **done**: `PUBLISHER_PUBLIC_KEY`
holds the production `loran-pages` key, distinct from
`signing::tests::TEST_PUBLIC_KEY`. Any future change to it is a key
rotation (§3 / §4), not a placeholder edit.

## 6. Release advisories

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ A separate verb (`loran help <tool>`) captures live `--help` output from any bin
|---|---|---|---|
| [`v0.1.0-ingot`](https://github.com/Spacecraft-Software/Loran/releases/tag/v0.1.0-ingot) | Ingot | Text-mode binary; 6 read verbs; bundled catalog. | 2026-05-12 |
| [`v0.2.0-billet`](https://github.com/Spacecraft-Software/Loran/releases/tag/v0.2.0-billet) | Billet | TUI; signed tarball updates; overlays; authoring. The 1.0-equivalent milestone. | 2026-05-12 |
| [`v0.3.0-bloom`](https://github.com/Spacecraft-Software/Loran/releases/tag/v0.3.0-bloom) | Bloom | Read-only MCP; JSON Schema; SFRS auto-ingestion; key-rotation primitive. | 2026-05-12 |
| [`v0.3.0-bloom`](https://github.com/Spacecraft-Software/Loran/releases/tag/v0.3.0-bloom) | Bloom | Read-only MCP; JSON Schema; CLI-Standard describe auto-ingestion; key-rotation primitive. | 2026-05-12 |

Twelve sub-commands functional, 26 curated pages bundled across 10 categories, full ratatui TUI, synchronous stdio JSON-RPC 2.0 MCP server, multi-platform CI matrix green (Linux gnu/musl/aarch64, FreeBSD cross-check, macOS arm64).

Expand Down Expand Up @@ -71,7 +71,7 @@ loran schema --json # emit Draft 2020-12 JSON Schema for the data model
loran categories # category registry with counts
```

Add `--json` to any read verb to get a structured envelope (per SFRS §6). Set `AI_AGENT=1` or `AGENT=1` in the environment and the TUI never activates — Loran emits JSON or a usage hint instead.
Add `--json` to any read verb to get a structured envelope (per the CLI Standard §6). Set `AI_AGENT=1` or `AGENT=1` in the environment and the TUI never activates — Loran emits JSON or a usage hint instead.

## Features

Expand All @@ -81,7 +81,7 @@ Add `--json` to any read verb to get a structured envelope (per SFRS §6). Set `
- **Agent-native by default** — `--json` on every read verb, `loran schema` emits Draft 2020-12 JSON Schema for every public type, `loran mcp` exposes a read-only MCP stdio server with the five read verbs.
- **No tokio anywhere.** Fully synchronous workspace, including the MCP server — one in-flight RPC at a time, blocking stdin reads.
- **TUI** built on `ratatui` + `crossterm` — browse view (categories ↔ tools), detail view (rendered / raw / frontmatter sub-views), `/`-fuzzy search, `?`-help overlay, panic-safe terminal restoration.
- **`DescribeIngestor`** — auto-synthesises pages from any SFRS-compliant tool's `<tool> describe --json` output (allowlisted via `LORAN_DESCRIBE_BINARIES`).
- **`DescribeIngestor`** — auto-synthesises pages from any CLI-Standard-compliant tool's `<tool> describe --json` output (allowlisted via `LORAN_DESCRIBE_BINARIES`).
- **Hermetic test seams** — every integration test sandboxes via env vars (`XDG_DATA_HOME`, `LORAN_DISTRO_OVERRIDE`, `LORAN_PAGES_*_URL`, …). No test ever writes to the real home.

## CLI reference
Expand All @@ -98,7 +98,7 @@ Add `--json` to any read verb to get a structured envelope (per SFRS §6). Set `
| `update` | `loran update` | Write | Refreshes upstream + tldr tarballs over HTTPS, verifies minisign. |
| `validate` | `loran validate` | Meta | Walks every overlay root; emits structured errors per file. |
| `schema` | `loran schema [--json]` | Meta | Draft 2020-12 JSON Schema for every public type. |
| `describe` | `loran describe [--json]` | Meta | SFRS describe manifest for agents. |
| `describe` | `loran describe [--json]` | Meta | The CLI Standard's describe manifest for agents. |
| `mcp` | `loran mcp` | Meta | Stdio JSON-RPC 2.0 MCP server (read-only verbs only). |
| _(default)_ | `loran` (no verb) | TUI | ratatui app when stdout is a TTY and no agent env detected. |

Expand Down Expand Up @@ -205,7 +205,7 @@ All key design decisions are locked in [`loran-spec-v0_2.md`](loran-spec-v0_2.md
From [`loran-spec-v0_2.md`](loran-spec-v0_2.md) §15:

1. **`pairs_with` reciprocity** — should `loran validate` warn when A claims `pairs_with = ["B"]` but B does not reciprocate, or accept asymmetry as intentional?
2. **`DescribeIngestor` security model** — allowlist (current default: env-driven via `LORAN_DESCRIBE_BINARIES`) vs self-declaration via SFRS `describe`?
2. **`DescribeIngestor` security model** — allowlist (current default: env-driven via `LORAN_DESCRIBE_BINARIES`) vs self-declaration via the CLI Standard's `describe`?

## Contributing

Expand Down
6 changes: 3 additions & 3 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ SPDX-FileCopyrightText: 2026 Mohamed Hammad

---
name: loran
description: Loran — the Spacecraft Software reference manual. Catalog-browse, curated tool pages, reverse legacy lookup, fuzzy search, and a deliberately-de-themed live `--help` capture. Agent-native (`--json`, `schema`, `describe`, read-only MCP) per Spacecraft Software SFRS v1.0.0.
description: Loran — the Spacecraft Software reference manual. Catalog-browse, curated tool pages, reverse legacy lookup, fuzzy search, and a deliberately-de-themed live `--help` capture. Agent-native (`--json`, `schema`, `describe`, read-only MCP) per Spacecraft Software CLI Standard v1.0.0.
license: GPL-3.0-or-later
project: Spacecraft Software
component: Loran
Expand All @@ -18,7 +18,7 @@ governing_docs:
- loran-todo-v0_1.md
governing_standards:
- Spacecraft Software Standard v1.1
- Spacecraft Software SFRS v1.0.0
- Spacecraft Software CLI Standard v1.0.0
entry_points:
- command: loran list
description: List tools in the catalog (filterable by --category, --replaces, --safe-alias-for).
Expand Down Expand Up @@ -73,7 +73,7 @@ entry_points:
idempotent: true
phase: 1 (placeholder), 3 (full)
- command: loran describe
description: Emit the live capability manifest per SFRS §4.
description: Emit the live capability manifest per the CLI Standard §4.
destructive: false
idempotent: true
phase: 1
Expand Down
35 changes: 35 additions & 0 deletions crates/loran-core/pages/audio/pactl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
+++
name = "pactl"
category = "audio"
summary = "PulseAudio control-protocol client. Drives sinks, sources, and modules on PulseAudio or pipewire-pulse."
replaces = []
safe_alias_for = []
pairs_with = ["wpctl"]
official = "https://www.freedesktop.org/wiki/Software/PulseAudio/"
tldr_page = "pactl"
written_in = "c"
since = "bravais@0.1"
tags = ["audio", "pulseaudio"]
aliases = []
+++

## Spacecraft Software notes

`pactl` speaks the PulseAudio control protocol. On a modern Spacecraft Software desktop that protocol is usually served by `pipewire-pulse`, so `pactl` and `wpctl` manipulate the *same* audio graph from two vocabularies. Prefer `wpctl` for native PipeWire work; reach for `pactl` when a tool, script, or upstream guide is written in PulseAudio terms.

## Recommended usage

```sh
pactl info # server, default sink/source
pactl list short sinks # enumerate outputs (name + index), tab-separated
pactl set-sink-volume @DEFAULT_SINK@ +5% # relative volume change
pactl set-sink-mute @DEFAULT_SINK@ toggle
pactl set-default-sink <NAME> # route the default output
pactl set-sink-input-volume <INPUT> 80% # per-application volume
```

Use the `list short` forms in scripts — they are tab-separated and stable; the long forms are for humans.

## Pairs with

- **wpctl** — the native PipeWire controller for the same graph; canonical for `set-default` and routing on PipeWire.
34 changes: 34 additions & 0 deletions crates/loran-core/pages/audio/wpctl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
+++
name = "wpctl"
category = "audio"
summary = "WirePlumber control. Spacecraft Software default for PipeWire volume, default sinks, and routing."
replaces = []
safe_alias_for = []
pairs_with = ["pactl"]
official = "https://pipewire.pages.freedesktop.org/wireplumber/"
tldr_page = "wpctl"
written_in = "c"
since = "bravais@0.1"
tags = ["audio", "pipewire"]
aliases = []
+++

## Spacecraft Software notes

`wpctl` is the WirePlumber session-manager controller and the Spacecraft Software-canonical way to drive audio on a PipeWire stack. It speaks to PipeWire natively — no PulseAudio shim — so it sees every node, sink, and source the graph exposes. Reach for `pactl` only when a tool or muscle-memory expects PulseAudio command shapes.

## Recommended usage

```sh
wpctl status # the full graph: sinks, sources, streams
wpctl get-volume @DEFAULT_AUDIO_SINK@ # current default-output volume
wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+ # nudge the default output up 5%
wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
wpctl set-default <ID> # make a sink/source the default (IDs from `status`)
```

`@DEFAULT_AUDIO_SINK@` / `@DEFAULT_AUDIO_SOURCE@` are stable aliases — bind them to media keys instead of hard-coding node IDs, which renumber across reboots.

## Pairs with

- **pactl** — the PulseAudio-compatible control surface over the same graph; use it when a script or guide is written against `pactl`.
35 changes: 35 additions & 0 deletions crates/loran-core/pages/bluetooth/bluetoothctl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
+++
name = "bluetoothctl"
category = "bluetooth"
summary = "BlueZ interactive client. Spacecraft Software default for scanning, pairing, and connecting devices."
replaces = []
safe_alias_for = []
pairs_with = ["btmgmt"]
official = "https://github.com/bluez/bluez"
tldr_page = "bluetoothctl"
written_in = "c"
since = "bravais@0.1"
tags = ["bluetooth", "bluez"]
aliases = []
+++

## Spacecraft Software notes

`bluetoothctl` is the interactive front-end to BlueZ over D-Bus and the Spacecraft Software-canonical tool for everyday pairing. It runs as a REPL, but every sub-command also works as a one-shot argument — which is what makes it scriptable.

## Recommended usage

```sh
bluetoothctl power on
bluetoothctl scan on # discover; `scan off` to stop
bluetoothctl devices # list known devices (MAC + name)
bluetoothctl pair AA:BB:CC:DD:EE:FF
bluetoothctl trust AA:BB:CC:DD:EE:FF # auto-reconnect on future boots
bluetoothctl connect AA:BB:CC:DD:EE:FF
```

For a guaranteed-clean pairing, `remove` the device first, then walk `scan on` → `pair` → `trust` → `connect`.

## Pairs with

- **btmgmt** — the lower-level, non-interactive management client; use it for adapter resets and scripted setup where a REPL is awkward.
36 changes: 36 additions & 0 deletions crates/loran-core/pages/bluetooth/btmgmt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
+++
name = "btmgmt"
category = "bluetooth"
summary = "BlueZ management-API client. Non-interactive adapter and device control for scripts and recovery."
replaces = []
safe_alias_for = []
pairs_with = ["bluetoothctl"]
official = "https://github.com/bluez/bluez"
# Empty: btmgmt has no tldr-pages entry, so this is the explicit
# "no tldr page" sentinel that disables the `loran show` tldr lookup.
tldr_page = ""
written_in = "c"
since = "bravais@0.1"
tags = ["bluetooth", "bluez"]
aliases = []
+++

## Spacecraft Software notes

`btmgmt` talks to the BlueZ management API directly rather than through the D-Bus agent layer that `bluetoothctl` uses. That makes it the right tool when the higher-level stack is wedged — toggling an adapter, forcing it discoverable, or driving setup from a non-interactive script.

## Recommended usage

```sh
btmgmt info # adapters and their current settings
btmgmt power on
btmgmt discov on # make the adapter discoverable
btmgmt find # scan for nearby devices
btmgmt pair -c <type> AA:BB:CC:DD:EE:FF
```

`btmgmt` usually needs root — it holds the management socket. When pairing works here but not in `bluetoothctl`, an agent/policy issue in the session layer is the likely cause.

## Pairs with

- **bluetoothctl** — the interactive, agent-backed client for day-to-day pairing; `btmgmt` is the escape hatch beneath it.
8 changes: 8 additions & 0 deletions crates/loran-core/pages/categories.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,11 @@ description = "Shells, prompts, and small productivity tools that improve daily
[data-processing]
title = "Data processing"
description = "JSON, YAML, CSV, and structured-data manipulation tools."

[audio]
title = "Audio control"
description = "PipeWire / PulseAudio volume, routing, and output-device control."

[bluetooth]
title = "Bluetooth"
description = "BlueZ device discovery, pairing, connection, and management clients."
Loading
Loading