[WARNING] No existing semantic tags found. Starting from v0.0.0

v0.0.1 - Reorganize ISO build tree, add docs/plans/scripts, and stage Phase 2 Slice B+C service integration artifacts
This commit is contained in:
Laan Tungir
2026-04-29 13:09:28 -04:00
parent dc7af63e6b
commit 6e2164abe5
151 changed files with 8865 additions and 1222 deletions

33
.gitignore vendored
View File

@@ -1 +1,32 @@
tutorial1/
tutorial1/
# Build artifacts
*.iso
*.img
*.squashfs
*.log
build.log
chroot/
binary/
.build/
cache/
# Live-build internal dirs
iso/.build/
iso/binary/
iso/chroot/
iso/cache/
iso/.stage/
iso/live-image-*
iso/chroot.*
iso/binary.modified_timestamps
iso/build.log
# Offline artifact staging for Slice B/C builds
iso/config/includes.chroot_before_packages/live-artifacts/
# Scratch area (legacy scripts; may contain secrets)
scratch/
# Editor/OS junk
.DS_Store

BIN
B650I Lightning WiFi.pdf Normal file

Binary file not shown.

1342
README.md

File diff suppressed because it is too large Load Diff

149
build.sh Executable file
View File

@@ -0,0 +1,149 @@
#!/bin/bash
# build.sh — build the n-OS-tr live ISO
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ISO_DIR="$REPO_ROOT/iso"
WORKSPACE_ISO="$WORKSPACE_ISO_DIR/live-image-amd64.hybrid.iso"
BUILD_LOG="$WORKSPACE_ISO_DIR/build.log"
ARTIFACTS_DIR="$WORKSPACE_ISO_DIR/config/includes.chroot_before_packages/live-artifacts"
CRELAY_SOURCE="$REPO_ROOT/includes/c_relay_static_x86_64"
NOSTR_ID_TUI_SOURCE_DIR="$REPO_ROOT/stack/nostr-id-tui"
NOSTR_ID_TUI_BINARY="$NOSTR_ID_TUI_SOURCE_DIR/build/nostr-id-tui_static_x86_64"
CLEAN_REQUESTED=0
if [[ "${1:-}" == "--clean" ]]; then
CLEAN_REQUESTED=1
shift
fi
if [[ "$#" -ne 0 ]]; then
echo "[build.sh] unsupported arguments: $*" >&2
echo "[build.sh] usage: ./build.sh [--clean]" >&2
exit 2
fi
home_opts="$(findmnt -n -o OPTIONS /home 2>/dev/null || true)"
if [[ "$home_opts" == *nodev* ]]; then
NEED_BIND=1
else
NEED_BIND=0
fi
cleanup_artifacts() {
echo "[build.sh] cleaning prior build artifacts in $WORKSPACE_ISO_DIR"
sudo rm -rf \
"$WORKSPACE_ISO_DIR/.build" \
"$WORKSPACE_ISO_DIR/cache" \
"$WORKSPACE_ISO_DIR/chroot" \
"$WORKSPACE_ISO_DIR/binary"
sudo rm -f \
"$WORKSPACE_ISO_DIR"/live-image-* \
"$WORKSPACE_ISO_DIR"/chroot.* \
"$WORKSPACE_ISO_DIR"/binary.modified_timestamps \
"$WORKSPACE_ISO_DIR"/build.log
}
stage_slice_bc_artifacts() {
echo "[build.sh] staging Slice B/C artifacts in $ARTIFACTS_DIR"
mkdir -p "$ARTIFACTS_DIR"
if [[ ! -f "$CRELAY_SOURCE" ]]; then
echo "[build.sh] ERROR: missing c-relay binary at $CRELAY_SOURCE" >&2
exit 1
fi
cp "$CRELAY_SOURCE" "$ARTIFACTS_DIR/c_relay_x86"
chmod 0755 "$ARTIFACTS_DIR/c_relay_x86"
echo "[build.sh] staged c-relay binary"
if ! command -v cargo-deb >/dev/null 2>&1; then
echo "[build.sh] ERROR: cargo-deb not installed. Run: cargo install cargo-deb" >&2
exit 1
fi
echo "[build.sh] building fips .deb"
(
cd "$REPO_ROOT/includes/fips"
./packaging/debian/build-deb.sh
)
FIPS_DEB="$(ls -1t "$REPO_ROOT"/includes/fips/deploy/fips_*_amd64.deb 2>/dev/null | head -1 || true)"
if [[ -z "$FIPS_DEB" || ! -f "$FIPS_DEB" ]]; then
echo "[build.sh] ERROR: no fips_*_amd64.deb produced in includes/fips/deploy" >&2
exit 1
fi
rm -f "$ARTIFACTS_DIR"/fips_*_amd64.deb
cp "$FIPS_DEB" "$ARTIFACTS_DIR/"
echo "[build.sh] staged $(basename "$FIPS_DEB")"
echo "[build.sh] building nostr-id-tui static musl binary"
(
cd "$NOSTR_ID_TUI_SOURCE_DIR"
./build_static.sh
)
if [[ ! -f "$NOSTR_ID_TUI_BINARY" ]]; then
echo "[build.sh] ERROR: nostr-id-tui static build produced no binary at $NOSTR_ID_TUI_BINARY" >&2
exit 1
fi
cp "$NOSTR_ID_TUI_BINARY" "$ARTIFACTS_DIR/nostr-id-tui_x86_64"
chmod 0755 "$ARTIFACTS_DIR/nostr-id-tui_x86_64"
echo "[build.sh] staged nostr-id-tui static binary"
}
if [[ "$CLEAN_REQUESTED" -eq 1 ]]; then
cleanup_artifacts
fi
SCRATCH=""
BUILD_DIR=""
CLEANUP() {
if [[ -n "$SCRATCH" ]]; then
if mountpoint -q "$SCRATCH"; then
sudo umount -R "$SCRATCH" 2>/dev/null || sudo umount -l "$SCRATCH" || true
fi
rmdir "$SCRATCH" 2>/dev/null || true
fi
}
trap CLEANUP EXIT
if [[ "$NEED_BIND" -eq 1 ]]; then
echo "[build.sh] /home has nodev mount option; building via bind-mounted scratch workspace"
SCRATCH="/var/tmp/n-os-tr-build-$$-$(date +%s)"
mkdir -p "$SCRATCH"
sudo mount --bind "$REPO_ROOT" "$SCRATCH"
echo "[build.sh] remounting scratch bind with dev to allow chroot /dev/null usage"
sudo mount -o remount,bind,dev "$SCRATCH"
scratch_opts="$(findmnt -n -o OPTIONS "$SCRATCH" 2>/dev/null || true)"
if [[ "$scratch_opts" == *nodev* ]]; then
echo "[build.sh] ERROR: scratch bind still has nodev after remount: $scratch_opts" >&2
echo "[build.sh] cannot safely run live-build chroot on a nodev mount" >&2
exit 1
fi
BUILD_DIR="$SCRATCH/iso"
else
echo "[build.sh] /home is not nodev; building directly in workspace"
BUILD_DIR="$WORKSPACE_ISO_DIR"
fi
if [[ ! -d "$BUILD_DIR" ]]; then
echo "[build.sh] build directory missing: $BUILD_DIR" >&2
exit 1
fi
stage_slice_bc_artifacts
cd "$BUILD_DIR"
sudo "$REPO_ROOT/lb-wrapper.sh" config
sudo "$REPO_ROOT/lb-wrapper.sh" build 2>&1 | tee "$BUILD_LOG"
if [[ ! -f "$WORKSPACE_ISO" ]]; then
echo "[build.sh] build produced no ISO at $WORKSPACE_ISO; see $BUILD_LOG" >&2
exit 1
fi
ls -lh "$WORKSPACE_ISO"

42
docs/RUNNING.md Normal file
View File

@@ -0,0 +1,42 @@
# Running n-OS-tr ISO (Phase 2 Slice B+C)
This slice boots a live system with these services enabled:
- `n-os-tr-firstboot.service` (oneshot): generates a per-boot ginxsom identity and self-signed TLS cert.
- `ginxsom.service`: Blossom FastCGI server on `/run/ginxsom/ginxsom-fcgi.sock`.
- `nginx.service`: TLS front-end on `https://localhost/`.
- `fips.service` + `fips-dns.service`: mesh transport and DNS responder.
- `c-relay.service`: Nostr relay behind nginx.
## Endpoints
- Landing page: `https://localhost/`
- Relay websocket: `wss://localhost/relay`
- Relay admin/API: `https://localhost/admin/`
## Retrieve generated identity
- Ginxsom pubkey and key hints are shown in `/etc/motd`.
- Provisioning logs: `journalctl -u n-os-tr-firstboot.service`
- Ginxsom nsec: `cat /var/lib/n-os-tr/keys/ginxsom.nsec`
- fips node identity (npub): `fipsctl identity`
- c-relay admin key from first boot logs: `journalctl -u c-relay.service | grep 'Admin Private Key:'`
## Smoke test
Run [`n-os-tr-smoketest`](iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) for a one-line-per-check health summary across core + blossom + fips + relay services, with output shaped like `[PASS|FAIL|SKIP] <label> (<detail>)` and a final PASS/FAIL/SKIP counter.
```bash
n-os-tr-smoketest
```
If terminal colours render poorly, use:
```bash
NO_COLOR=1 n-os-tr-smoketest
```
## Notes
- Identity is ephemeral by default in this slice.
- fips is configured for ethernet discovery and a static UDP bootstrap peer.

913
docs/debian_live_notes.md Normal file
View File

@@ -0,0 +1,913 @@
# Debian Live Build Notes
> **Archived developer notes.** This document was the original [`README.md`](../README.md) for this repository when it was primarily a Debian live-build tutorial. It has been moved here to preserve the reference material now that [`README.md`](../README.md) focuses on the n-OS-tr product vision.
>
> Nothing in this document is a contract — it is a collection of live-build recipes, package-discovery tips, GUI options, minimization tactics, and persistence notes we accumulated while bootstrapping the ISO build. Anything that is part of the real product lives elsewhere (typically in [`plans/`](../plans/) or under [`iso/`](../iso/)).
## Debian Live Build Setup and Tutorial 1 Implementation
This document covers the complete process of setting up live-build and implementing Tutorial 1 from the Debian Live Manual.
### System Requirements
Tested on Pop!_OS 22.04 LTS (Ubuntu-based system).
### Phase 1: Installing Dependencies
The following packages are required for building Debian Live images:
```bash
sudo apt update
sudo apt install debootstrap xorriso squashfs-tools syslinux-utils cpio gzip gettext
```
**Package purposes:**
- `debootstrap`: Creates minimal Debian base system
- `xorriso`: Creates ISO images
- `squashfs-tools`: Creates compressed filesystem
- `syslinux-utils`: Boot loader utilities
- `cpio`, `gzip`, `gettext`: Archive and localization utilities
### Phase 2: Setting up Live-Build from Repository
Instead of installing live-build via system packages (which may be outdated), we use the repository directly for the most current version.
#### Creating the Wrapper Script
Create `lb-wrapper.sh` for convenient access to live-build commands:
```bash
#!/bin/bash
# Set the LIVE_BUILD environment variable to point to our repository
export LIVE_BUILD="$(dirname "$0")/live-build"
# Execute the live-build frontend with all arguments passed through
"$LIVE_BUILD/frontend/lb" "$@"
```
Make it executable:
```bash
chmod +x lb-wrapper.sh
```
**Usage:** Replace all `lb` commands with `./lb-wrapper.sh` to use the repository version.
### Phase 3: Tutorial 1 - Default Image Creation
Following the Debian Live Manual Tutorial 1 to create a basic live system:
```bash
mkdir tutorial1
cd tutorial1
../lb-wrapper.sh config
```
This creates the basic configuration structure in the `config/` directory.
### Phase 4: Building the Image
**Important:** Due to keyring compatibility issues between Ubuntu/Pop!_OS and Debian, we need to bypass GPG verification for development builds.
Build the image as root with security bypass:
```bash
sudo ../lb-wrapper.sh config --apt-secure false --distribution stable
sudo ../lb-wrapper.sh build 2>&1 | tee build.log
```
### Keyring Issues and Troubleshooting
**Problem:** Pop!_OS/Ubuntu keyrings are incompatible with both Debian testing and stable distributions, causing GPG verification failures.
**Error examples:**
- Debian testing: `The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 78DBA3BC47EF2265`
- Debian stable: `The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 762F67A0B2C39DE4`
**Solution:** Use `--apt-secure false` flag to bypass GPG verification for development purposes.
**Production note:** For production builds, proper keyring setup should be implemented instead of disabling security checks.
### Expected Results
- Build time: Varies based on internet connection and system performance
- Output file: `live-image-amd64.hybrid.iso` (approximately 298MB)
- This ISO can be:
- Tested in virtual machines (QEMU, VirtualBox)
- Written to USB drives
- Burned to optical media
### Command Summary
Complete sequence for Tutorial 1:
```bash
# 1. Install dependencies
sudo apt install debootstrap xorriso squashfs-tools syslinux-utils cpio gzip gettext
# 2. Create wrapper script (if not exists)
# [Create lb-wrapper.sh as shown above]
chmod +x lb-wrapper.sh
# 3. Create and configure tutorial
mkdir tutorial1
cd tutorial1
../lb-wrapper.sh config --apt-secure false --distribution stable
# 4. Build image
sudo ../lb-wrapper.sh build 2>&1 | tee build.log
# 5. Result: live-image-amd64.hybrid.iso ready for testing
```
### Notes
- The `--distribution stable` ensures we use Debian stable instead of the default testing
- Build logs are automatically saved due to the wrapper script configuration
- The `--apt-secure false` flag is a workaround for keyring issues on Ubuntu-based systems
- For clean rebuilds, use `sudo ../lb-wrapper.sh clean` before building again
## FAQ
### Q: What are the auto, config, and local directories created by `lb config` for?
**A:** When you run `lb config`, three directories are created:
1. **auto/** - Created but empty by default
- Used for optional automation scripts that you create yourself
- You can add `auto/config`, `auto/build`, `auto/clean` scripts that run automatically before their respective `lb` commands
- Most basic builds (like Tutorial 1) don't need these files
- Useful for advanced scenarios where you want reproducible builds with version control
2. **config/** - This is where the real configuration lives
- Contains all the subdirectories for customizing your live system
- Has the main config files (binary, bootstrap, chroot, common, source) with your build parameters
- This is where you add package lists, custom files, hooks, etc.
- Most important directory for customization
3. **local/** - For local customizations
- Also typically empty unless you have custom executables or files to include
- Used for build-time scripts and tools, not files to include in the final system
### Q: How do I add files to specific locations in the final live system?
**A:** Several directories in the config structure map directly to the final system filesystem:
**For files you want in the final live system:**
1. **config/includes.chroot/** - Maps directly to the root filesystem (`/`)
- To put a file in `/usr/local/bin/myscript` in the final system
- Place it at `config/includes.chroot/usr/local/bin/myscript`
- Everything in this directory gets copied to `/` in the live system
2. **config/includes.chroot_after_packages/** - Same mapping, but copied after packages are installed
- Use when you want to overwrite files that packages might have installed
- Same directory mapping as above
3. **config/includes.chroot_before_packages/** - Copied before packages are installed
- Less commonly used, but available if needed
**For files you want on the ISO/boot media itself (not in the live system):**
4. **config/includes.binary/** - Files that go on the boot media
- These appear on the USB/CD itself, not inside the running live system
- Useful for documentation, additional tools, etc.
**Example:** To put a custom script in `/usr/local/bin/hello` in your live system:
```bash
# 1. Create the directory structure
mkdir -p config/includes.chroot/usr/local/bin/
# 2. Put your script there
cp myscript config/includes.chroot/usr/local/bin/hello
# 3. Make it executable
chmod +x config/includes.chroot/usr/local/bin/hello
```
The build process will automatically copy it to the correct location in the live system.
### Q: How would I add a splash screen that would come up upon booting?
**A:** There are several methods to add splash screens to your Debian Live system, depending on when and how you want the splash to appear:
#### Method 1: Boot Loader Splash (Easiest - SYSLINUX)
For a simple graphical background during boot menu selection:
**1. Prepare your splash image:**
```bash
# Create the bootloader directory
mkdir -p config/bootloaders/syslinux
# Your image should be:
# - PNG or JPEG format
# - 640x480 resolution (for best compatibility)
# - Named something like splash.png
cp your-splash-image.png config/bootloaders/syslinux/splash.png
```
**2. Create custom syslinux configuration:**
```bash
# Copy the default syslinux config to customize
cp live-build/share/bootloaders/syslinux/* config/bootloaders/syslinux/
# Edit config/bootloaders/syslinux/syslinux.cfg
# Add this line after the first line:
# MENU BACKGROUND splash.png
```
#### Method 2: Plymouth Boot Splash (Advanced - Animated)
For professional animated boot screens like Ubuntu's spinning logo:
**1. Add Plymouth to your package list:**
```bash
echo "plymouth plymouth-themes" >> config/package-lists/splash.list.chroot
```
**2. Configure Plymouth theme:**
```bash
# Create Plymouth configuration directory
mkdir -p config/includes.chroot/etc/plymouth
# Set the default theme (example: solar theme)
echo "[Daemon]" > config/includes.chroot/etc/plymouth/plymouthd.conf
echo "Theme=solar" >> config/includes.chroot/etc/plymouth/plymouthd.conf
```
**3. Enable Plymouth in boot parameters:**
```bash
# When running lb config, add:
../lb-wrapper.sh config --bootappend-live "boot=live components splash quiet"
```
#### Method 3: GRUB Graphical Splash (For EFI systems)
For systems using GRUB (EFI boot):
**1. Create GRUB bootloader directory:**
```bash
mkdir -p config/bootloaders/grub-pc
```
**2. Add custom GRUB background:**
```bash
# Copy your background image (PNG, JPG, or TGA format)
# Recommended size: 1024x768 or your target resolution
cp your-background.png config/includes.binary/boot/grub/background.png
```
**3. Create custom GRUB configuration:**
```bash
# Create config/bootloaders/grub-pc/grub.cfg with:
cat > config/bootloaders/grub-pc/grub.cfg << 'EOF'
if background_image /boot/grub/background.png; then
set color_normal=white/black
set color_highlight=black/light-gray
else
set menu_color_normal=cyan/blue
set menu_color_highlight=white/blue
fi
EOF
```
#### Method 4: Desktop Splash Screen
For splash screens that appear when the desktop environment starts:
**1. Add to package list (example with LXDE):**
```bash
echo "lxsplash" >> config/package-lists/desktop.list.chroot
```
**2. Configure desktop splash:**
```bash
# Create desktop configuration directory
mkdir -p config/includes.chroot/etc/xdg/lxsession/LXDE
# Configure lxsession to show splash
echo "lxsplash -s" > config/includes.chroot/etc/xdg/lxsession/LXDE/autostart
```
#### Quick Start Recommendation
**For beginners:** Start with Method 1 (SYSLINUX background) - it's simple and works reliably.
**Example complete setup:**
```bash
# 1. Create bootloader directory and add image
mkdir -p config/bootloaders/syslinux
cp ~/my-logo.png config/bootloaders/syslinux/splash.png
# 2. Copy default syslinux files to customize
cp -r live-build/share/bootloaders/syslinux/* config/bootloaders/syslinux/
# 3. Edit the main config file
sed -i '2i MENU BACKGROUND splash.png' config/bootloaders/syslinux/syslinux.cfg
# 4. Build with your custom splash
sudo ../lb-wrapper.sh build
```
**Notes:**
- Image formats: PNG works best for syslinux, JPG for GRUB
- Keep images reasonably sized to avoid slowing boot time
- Test your splash screen in a virtual machine first
- For production systems, ensure images are properly licensed
### Q: What are the main options for adding a GUI? How do I add them?
**A:** Debian Live supports several GUI options, from lightweight window managers to full desktop environments. Here are the main options and how to implement them:
#### Lightweight Window Managers (Minimal Resource Usage)
##### 1. Openbox (Very Light)
```bash
echo "openbox obconf obmenu tint2 pcmanfm lxappearance" >> config/package-lists/gui.list.chroot
```
**Features:** Basic window manager, highly customizable, ~50MB additional space
**Best for:** Minimal systems, older hardware, custom interfaces
##### 2. i3 (Tiling Window Manager)
```bash
echo "i3 i3status dmenu i3lock" >> config/package-lists/gui.list.chroot
```
**Features:** Tiling window manager, keyboard-driven, very efficient
**Best for:** Power users, developers, keyboard-focused workflows
##### 3. Fluxbox
```bash
echo "fluxbox fbpanel pcmanfm" >> config/package-lists/gui.list.chroot
```
**Features:** Fast, simple, tabbed windows
**Best for:** Minimal desktop needs, simple interfaces
#### Desktop Environments (Full-Featured)
##### 4. LXDE (Lightweight Desktop Environment)
```bash
echo "task-lxde-desktop" >> config/package-lists/gui.list.chroot
```
**Features:** Complete desktop with panel, file manager, settings
**Size:** ~400MB additional
**Best for:** Beginner-friendly, low resource usage, complete desktop experience
##### 5. Xfce (Moderate Resource Usage)
```bash
echo "task-xfce-desktop" >> config/package-lists/gui.list.chroot
```
**Features:** Full-featured, customizable, good performance
**Size:** ~600MB additional
**Best for:** Balance between features and performance
##### 6. GNOME (Full-Featured)
```bash
echo "task-gnome-desktop" >> config/package-lists/gui.list.chroot
```
**Features:** Modern interface, many applications, polished experience
**Size:** ~1.5GB additional
**Best for:** Modern desktop experience, touch interfaces, latest features
##### 7. KDE Plasma (Full-Featured)
```bash
echo "task-kde-desktop" >> config/package-lists/gui.list.chroot
```
**Features:** Highly customizable, many applications, modern interface
**Size:** ~1.2GB additional
**Best for:** Power users who want customization and modern features
#### Quick Setup Examples
##### Example 1: Minimal GUI System (LXDE + Firefox)
```bash
# Create tutorial directory
mkdir tutorial-gui
cd tutorial-gui
# Configure with GUI
../lb-wrapper.sh config --apt-secure false --distribution stable
# Add desktop and browser
echo "task-lxde-desktop firefox-esr" >> config/package-lists/desktop.list.chroot
# Optional: Add useful applications
echo "synaptic gdebi leafpad galculator" >> config/package-lists/apps.list.chroot
# Build
sudo ../lb-wrapper.sh build
```
##### Example 2: Kiosk System (Single Application)
```bash
# Minimal X11 setup for single application
echo "xorg gdm3 openbox firefox-esr" >> config/package-lists/kiosk.list.chroot
# Auto-start Firefox in kiosk mode
mkdir -p config/includes.chroot/etc/skel
cat > config/includes.chroot/etc/skel/.xsession << 'EOF'
#!/bin/sh
openbox &
firefox --kiosk https://example.com
EOF
```
##### Example 3: Developer Workstation
```bash
# Add Xfce desktop with development tools
echo "task-xfce-desktop" >> config/package-lists/desktop.list.chroot
echo "code git build-essential python3 nodejs npm" >> config/package-lists/dev.list.chroot
echo "terminator vim emacs" >> config/package-lists/editors.list.chroot
```
#### GUI Components Explanation
##### Essential X11 Components (Required for any GUI)
```bash
# Always needed for graphical interface
echo "xorg" >> config/package-lists/x11.list.chroot
```
##### Display Managers (Login Screen)
```bash
# Lightweight
echo "lightdm" >> config/package-lists/dm.list.chroot
# GNOME/modern
echo "gdm3" >> config/package-lists/dm.list.chroot
# Simple
echo "slim" >> config/package-lists/dm.list.chroot
```
##### Common Applications by Category
```bash
# File managers
echo "pcmanfm nautilus thunar" >> config/package-lists/filemanagers.list.chroot
# Text editors
echo "gedit mousepad kate" >> config/package-lists/editors.list.chroot
# Web browsers
echo "firefox-esr chromium" >> config/package-lists/browsers.list.chroot
# Media players
echo "vlc mpv rhythmbox" >> config/package-lists/media.list.chroot
# Graphics
echo "gimp inkscape" >> config/package-lists/graphics.list.chroot
```
#### Resource Usage Comparison
| Desktop Environment | RAM Usage | Disk Space | Boot Time | Complexity |
|-------------------|-----------|------------|-----------|------------|
| Openbox alone | ~100MB | ~50MB | Fast | Simple |
| LXDE | ~200MB | ~400MB | Fast | Easy |
| Xfce | ~300MB | ~600MB | Medium | Medium |
| GNOME | ~800MB | ~1.5GB | Slow | Complex |
| KDE Plasma | ~600MB | ~1.2GB | Medium | Complex |
#### Advanced GUI Configuration
##### Custom Desktop Theme
```bash
# Create theme directory
mkdir -p config/includes.chroot/usr/share/themes/MyTheme
# Add custom wallpaper
mkdir -p config/includes.chroot/usr/share/pixmaps
cp my-wallpaper.jpg config/includes.chroot/usr/share/pixmaps/
# Set default wallpaper for LXDE
mkdir -p config/includes.chroot/etc/xdg/pcmanfm/LXDE
cat > config/includes.chroot/etc/xdg/pcmanfm/LXDE/desktop-items-0.conf << 'EOF'
[*]
wallpaper_mode=stretch
wallpaper_common=1
wallpaper=/usr/share/pixmaps/my-wallpaper.jpg
EOF
```
##### Auto-login Configuration
```bash
# For LXDE with automatic login
mkdir -p config/includes.chroot/etc/lightdm
cat > config/includes.chroot/etc/lightdm/lightdm.conf << 'EOF'
[Seat:*]
autologin-user=user
autologin-user-timeout=0
EOF
```
#### Recommendation Guide
**For new users:** Start with LXDE (`task-lxde-desktop`)
- Easy to use
- Good performance
- Includes essential applications
- Well documented
**For minimal systems:** Use Openbox + essential apps
- Very low resource usage
- Fast boot times
- Customizable
**For modern experience:** Choose Xfce or GNOME
- Modern interface
- Good hardware support
- Regular updates
**For specific use cases:** Custom combinations
- Kiosk systems: Openbox + single application
- Developer workstations: Xfce + development tools
- Media centers: Minimal WM + media applications
### Q: How do I look up applications on my current system to get package names for config/package-lists?
**Q: I want to include applications I currently use in my live system. How do I find the exact package names to add to my config/package-lists files?**
**A:** There are several methods to discover package names from installed applications. Here are the most effective approaches:
#### Method 1: List All Installed Packages
##### Using dpkg (Debian Package Manager)
```bash
# List all installed packages with descriptions
dpkg -l | grep -i "search-term"
# Examples:
dpkg -l | grep -i firefox # Find Firefox packages
dpkg -l | grep -i editor # Find text editors
dpkg -l | grep -i media # Find media applications
```
##### Using apt
```bash
# List all manually installed packages
apt list --installed | grep -i "search-term"
# Show only manually installed (not dependencies)
apt-mark showmanual | grep -i "search-term"
# Examples:
apt list --installed | grep -i gimp
apt-mark showmanual | grep -i code
```
#### Method 2: Find Package for Specific Application
##### Using which and dpkg
```bash
# Find which package provides a specific command
which firefox
dpkg -S $(which firefox)
# Or in one command:
dpkg -S $(which firefox) | cut -d: -f1
# Examples:
dpkg -S $(which code) # Find VS Code package
dpkg -S $(which vlc) # Find VLC package
dpkg -S $(which gimp) # Find GIMP package
```
##### Using apt-file (if installed)
```bash
# First install and update apt-file
sudo apt install apt-file
sudo apt-file update
# Find package containing a file
apt-file search /usr/bin/firefox
apt-file search gimp
# Find package containing any file with name
apt-file search -x '/usr/bin/.*firefox.*'
```
#### Method 3: Application-Specific Discovery
##### Desktop Applications (.desktop files)
```bash
# List all desktop applications
ls /usr/share/applications/*.desktop | while read app; do
name=$(basename "$app" .desktop)
echo "Desktop entry: $name"
# Find which package provides this .desktop file
dpkg -S "$app" 2>/dev/null | cut -d: -f1
done
```
##### GUI Application Discovery
```bash
# Find packages by searching application menu names
dpkg -l | awk '/^ii/{print $2}' | while read pkg; do
if dpkg -L "$pkg" 2>/dev/null | grep -q "/usr/share/applications/"; then
echo "Package with GUI: $pkg"
fi
done
```
#### Method 4: Category-Based Package Discovery
##### By Package Sections
```bash
# Find packages by category
dpkg-query -W -f='${Package} ${Section}\n' | grep -E "(graphics|video|audio|editors|web|games)" | sort
# Specific categories:
dpkg-query -W -f='${Package} ${Section}\n' | grep graphics
dpkg-query -W -f='${Package} ${Section}\n' | grep editors
dpkg-query -W -f='${Package} ${Section}\n' | grep web
```
##### By Description Content
```bash
# Search package descriptions for keywords
apt-cache search "text editor" | head -10
apt-cache search "media player" | head -10
apt-cache search "graphics" | head -10
apt-cache search "development" | head -10
```
#### Method 5: Practical Examples
##### Common Applications and Their Package Names
**Web Browsers:**
```
Firefox → firefox-esr
Google Chrome → google-chrome-stable (requires external repository)
Chromium → chromium
```
**Text Editors:**
```
VS Code → code (requires external repository)
Sublime Text → sublime-text (requires external repository)
Vim → vim
Emacs → emacs
Nano → nano
Gedit → gedit
```
**Media Applications:**
```
VLC → vlc
GIMP → gimp
Inkscape → inkscape
Audacity → audacity
OBS Studio → obs-studio
```
**Development Tools:**
```
Git → git
Node.js → nodejs
Python → python3
Docker → docker.io
```
#### Method 6: Generate Package List from Current System
##### Create Complete Package List
```bash
# Generate a list of all manually installed packages
apt-mark showmanual > my-current-packages.txt
# Clean up the list (remove system packages you don't want)
# Then use in your live-build:
cp my-current-packages.txt config/package-lists/current-system.list.chroot
```
##### Create Filtered Package List
```bash
# Create list excluding system packages
apt-mark showmanual | grep -v -E "^(linux-|grub-|systemd|udev|apt|dpkg)" > config/package-lists/applications.list.chroot
```
#### Method 7: Interactive Package Discovery
##### Using Synaptic Package Manager
```bash
# Install Synaptic for GUI package browsing
sudo apt install synaptic
# Launch Synaptic, browse categories, note package names
# Then add them to your package lists
```
##### Using aptitude (Text UI)
```bash
# Install aptitude for interactive package management
sudo apt install aptitude
# Launch aptitude, browse packages interactively
aptitude
```
#### Tips and Best Practices
**Package Name Variations:**
- Some applications have different names in Debian repositories
- Use `apt search application-name` to find alternatives
- Check both the main package and `-dev` packages if you need development files
**Metapackages vs Individual Packages:**
- `task-*` packages install complete desktop environments
- Individual packages give more control over what's installed
- You can mix both approaches in different `.list.chroot` files
**External Repositories:**
- Some applications (VS Code, Chrome) require external repositories
- Consider using alternative packages available in Debian repos (Chromium instead of Chrome)
- Document external repository requirements separately
**Testing Package Lists:**
- Test your package lists in a virtual machine before final build
- Use `apt-cache policy package-name` to verify package availability
- Check package dependencies with `apt-cache depends package-name`
### Q: This is live-build, so it runs in memory. Can you also install it to disk if you want?
**A:** Yes! Debian Live systems are designed to run both ways - as temporary live systems in memory AND as permanent installations to disk.
#### Live vs Installed - The Difference
**Live Mode (Default):**
- Runs entirely from USB/CD/DVD in RAM
- No changes are saved (unless using persistence - see below)
- Perfect for testing, troubleshooting, portable computing
- No installation required - boot and go
**Installed Mode:**
- Permanently installed to hard disk like any other OS
- All changes saved normally
- Full desktop OS experience
- Can dual-boot with other operating systems
#### Installation Methods
##### Method 1: Include Debian Installer (Recommended)
Add the installer to your live build configuration:
```bash
# When configuring your build, enable the installer
../lb-wrapper.sh config --debian-installer live --apt-secure false --distribution stable
# Add installer launcher to desktop
echo "debian-installer-launcher" >> config/package-lists/installer.list.chroot
```
##### Method 2: Text-based Installer
```bash
# Add text installer to config
../lb-wrapper.sh config --debian-installer true --apt-secure false --distribution stable
```
##### Method 3: Live-Installer (Simpler)
```bash
# Add live-installer package
echo "live-installer" >> config/package-lists/installer.list.chroot
```
#### Persistence (Hybrid Approach)
##### Create Persistent USB
```bash
# Create persistence partition on USB after writing ISO
# (This example assumes USB device is /dev/sdb - VERIFY YOUR DEVICE!)
# 1. Write ISO to USB first
sudo dd if=live-image-amd64.hybrid.iso of=/dev/sdb bs=4M status=progress
# 2. Create additional partition for persistence
sudo fdisk /dev/sdb
# Create new partition using remaining space
# 3. Format persistence partition
sudo mkfs.ext4 -L persistence /dev/sdb2
# 4. Mount and create persistence.conf
sudo mkdir -p /mnt/persistence
sudo mount /dev/sdb2 /mnt/persistence
echo "/ union" | sudo tee /mnt/persistence/persistence.conf
sudo umount /mnt/persistence
```
##### Enable Persistence at Boot
```bash
# Add to boot parameters
boot=live components persistence
```
> **Note for n-OS-tr**: persistence is **explicitly discouraged** for this project — see [`plans/threat_model.md`](../plans/threat_model.md) for why ephemerality is a privacy property we refuse to weaken by default. Persistence is kept here only as reference material.
### Q: How small can you go?
**A:** System minimization in Debian Live is possible, but there are important limitations. Core utilities like `ls` are part of essential packages that cannot be safely removed.
#### Understanding Essential vs Optional Packages
**Essential Packages (Cannot Remove):**
- **coreutils** (includes `ls`, `cp`, `mv`, `cat`, `mkdir`, etc.)
- **bash** (shell interpreter)
- **init systems** (systemd or equivalent)
- **libc6** (core C library)
- **dpkg** (package manager)
**Why `ls` Cannot Be Removed:**
```bash
dpkg -S $(which ls)
# Output: coreutils: /bin/ls
dpkg -s coreutils | grep Essential
# Output: Essential: yes
```
Essential packages are marked as such because removing them would break the system fundamentally.
#### Minimization Strategies
##### Strategy 1: Minimal Debootstrap (Recommended)
```bash
../lb-wrapper.sh config \
--apt-secure false \
--distribution stable \
--debootstrap-options "--variant=minbase" \
--apt-recommends false \
--apt-indices false \
--firmware-chroot false \
--memtest none \
--binary-image hdd
echo "user-setup sudo" > config/package-lists/essential.list.chroot
echo "ifupdown isc-dhcp-client" >> config/package-lists/essential.list.chroot
```
**Results:** ~298MB system (compared to ~380MB default)
##### Strategy 2: Remove Non-Essential Recommended Packages
```bash
../lb-wrapper.sh config --apt-recommends false
echo "live-tools eject" > config/package-lists/minimal-live.list.chroot
```
##### Strategy 3: Custom Package Exclusions
```bash
mkdir -p config/hooks/normal
cat > config/hooks/normal/0010-remove-optional.hook.chroot << 'EOF'
#!/bin/bash
# Remove documentation to save space
rm -rf /usr/share/doc/*
rm -rf /usr/share/man/*
rm -rf /usr/share/info/*
# Remove locale data except for English
find /usr/share/locale -mindepth 1 -maxdepth 1 -type d ! -name 'en*' -exec rm -rf {} \;
# Remove cached package files
apt-get clean
EOF
```
**Warning:** This violates package integrity and may cause issues.
#### Size Comparison Table
| Configuration Type | Size | Boot Time | Functionality |
|-------------------|------|-----------|---------------|
| Default Tutorial 1 | ~380MB | Medium | Basic console + live tools |
| Minimal (minbase) | ~280MB | Fast | Essential console only |
| Ultra-minimal hook | ~250MB | Very fast | Bare minimum (risky) |
| Minimal GUI | ~450MB | Medium | Basic graphical interface |
| Full LXDE | ~800MB | Slow | Complete desktop |
#### The Bottom Line: Practical Limits
- **Theoretical minimum:** ~180-200MB (unstable, breaks functionality)
- **Practical minimum:** ~250-280MB (stable, console-only)
- **Recommended minimum:** ~350-400MB (includes basic GUI capability)
#### Best Practices for Minimization
1. **Start with `--debootstrap-options "--variant=minbase"`** — safest reduction
2. **Disable recommendations** with `--apt-recommends false`
3. **Remove documentation/locales** via hooks if space is critical
4. **Test thoroughly** — minimal systems may lack expected tools
5. **Document what you removed** for troubleshooting
6. **Consider alternatives** like Alpine Linux for truly minimal containers
**Remember:** Core utilities like `ls`, `cp`, `mv` are fundamental to Unix-like systems. Focus on removing optional packages, documentation, and recommended packages instead of core system utilities.
The goal should be "minimal but functional" rather than "absolutely smallest possible" for any system you plan to actually use.

1
includes/c-relay Submodule

Submodule includes/c-relay added at 2d2ca79dfd

Binary file not shown.

1
includes/fips Submodule

Submodule includes/fips added at be0708ac9b

1
includes/ginxsom Submodule

Submodule includes/ginxsom added at 9491f5c373

Binary file not shown.

Submodule includes/nostr_core_lib added at 7101502fbe

201
increment_and_push.sh Executable file
View File

@@ -0,0 +1,201 @@
#!/bin/bash
# increment_and_push.sh - Version increment and git automation script for n_os_tr
# Usage:
# ./increment_and_push.sh "meaningful git comment"
# ./increment_and_push.sh --set-version vX.Y.Z "meaningful git comment"
# ./increment_and_push.sh -m "meaningful git comment" # minor bump
# ./increment_and_push.sh -M "meaningful git comment" # major bump
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
show_usage() {
cat <<EOF
n_os_tr increment_and_push.sh
USAGE:
$0 [OPTIONS] "commit message"
OPTIONS:
-p, --patch Increment patch version (default)
-m, --minor Increment minor version
-M, --major Increment major version
--set-version vX.Y.Z Set explicit version
-h, --help Show this help
EXAMPLES:
$0 "Phase update with service hardening"
$0 -m "Ship identity subsystem scaffolding"
$0 --set-version v1.0.0 "First stable release"
EOF
}
check_git_repo() {
if ! git rev-parse --git-dir >/dev/null 2>&1; then
print_error "Not in a git repository"
exit 1
fi
}
current_branch() {
git branch --show-current
}
latest_version_tag() {
git tag -l 'v*.*.*' | sort -V | tail -n 1
}
compute_next_version() {
local bump_type="$1"
local latest_tag
latest_tag="$(latest_version_tag)"
if [[ -z "$latest_tag" ]]; then
latest_tag="v0.0.0"
print_warning "No existing semantic tags found. Starting from $latest_tag"
fi
local version="${latest_tag#v}"
if [[ ! "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
print_error "Latest tag '$latest_tag' is not in vX.Y.Z format"
exit 1
fi
local major="${BASH_REMATCH[1]}"
local minor="${BASH_REMATCH[2]}"
local patch="${BASH_REMATCH[3]}"
case "$bump_type" in
major)
major=$((major + 1))
minor=0
patch=0
;;
minor)
minor=$((minor + 1))
patch=0
;;
patch)
patch=$((patch + 1))
;;
*)
print_error "Unknown bump type: $bump_type"
exit 1
;;
esac
echo "v${major}.${minor}.${patch}"
}
COMMIT_MESSAGE=""
TARGET_VERSION=""
BUMP_TYPE="patch"
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--patch)
BUMP_TYPE="patch"
shift
;;
-m|--minor)
BUMP_TYPE="minor"
shift
;;
-M|--major)
BUMP_TYPE="major"
shift
;;
--set-version)
if [[ $# -lt 2 ]]; then
print_error "--set-version requires a value like v1.2.3"
exit 1
fi
TARGET_VERSION="$2"
shift 2
;;
-h|--help)
show_usage
exit 0
;;
*)
if [[ -z "$COMMIT_MESSAGE" ]]; then
COMMIT_MESSAGE="$1"
else
COMMIT_MESSAGE+=" $1"
fi
shift
;;
esac
done
if [[ -z "$COMMIT_MESSAGE" ]]; then
print_error "Commit message is required"
show_usage
exit 1
fi
check_git_repo
if [[ -n "$TARGET_VERSION" ]]; then
if [[ ! "$TARGET_VERSION" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
print_error "Invalid --set-version value '$TARGET_VERSION' (expected vX.Y.Z)"
exit 1
fi
NEW_VERSION="$TARGET_VERSION"
else
NEW_VERSION="$(compute_next_version "$BUMP_TYPE")"
fi
print_info "Target version: $NEW_VERSION"
print_info "Staging changes..."
git add -A
if git diff --staged --quiet; then
print_warning "No staged changes to commit"
exit 0
fi
FULL_COMMIT_MESSAGE="$NEW_VERSION - $COMMIT_MESSAGE"
print_info "Committing: $FULL_COMMIT_MESSAGE"
git commit -m "$FULL_COMMIT_MESSAGE"
if git rev-parse "$NEW_VERSION" >/dev/null 2>&1; then
print_warning "Tag $NEW_VERSION already exists locally. Re-pointing to current commit."
git tag -d "$NEW_VERSION" >/dev/null 2>&1 || true
fi
print_info "Creating tag: $NEW_VERSION"
git tag "$NEW_VERSION"
BRANCH="$(current_branch)"
if [[ -z "$BRANCH" ]]; then
print_error "Could not determine current branch"
exit 1
fi
print_info "Pushing branch '$BRANCH'..."
if ! git push origin "$BRANCH"; then
print_warning "Initial push failed. Attempting rebase onto origin/$BRANCH and retrying..."
git pull --rebase origin "$BRANCH"
git push origin "$BRANCH"
fi
print_info "Pushing tag '$NEW_VERSION'..."
if ! git push origin "$NEW_VERSION"; then
print_warning "Tag push failed; attempting force update for tag '$NEW_VERSION'"
git push --force origin "$NEW_VERSION"
fi
print_success "Done: committed and pushed $NEW_VERSION"

19
iso/auto/config Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/sh
# iso/auto/config — run by `lb config` inside iso/
set -e
lb config noauto \
--mode debian \
--distribution bookworm \
--architectures amd64 \
--binary-images iso-hybrid \
--archive-areas "main contrib non-free non-free-firmware" \
--apt-indices false \
--apt-recommends true \
--apt-secure false \
--memtest none \
--iso-application "n-OS-tr" \
--iso-volume "n-OS-tr" \
--iso-publisher "n-OS-tr project" \
--bootappend-live "boot=live components console=tty0 console=ttyS0,115200n8 loglevel=4" \
"${@}"

View File

@@ -7,10 +7,10 @@ LB_IMAGE_TYPE="iso-hybrid"
LB_BINARY_FILESYSTEM="fat32"
# Set apt/aptitude generic indices
LB_APT_INDICES="true"
LB_APT_INDICES="false"
# Set boot parameters
LB_BOOTAPPEND_LIVE="boot=live components quiet splash"
LB_BOOTAPPEND_LIVE="boot=live components console=tty0 console=ttyS0,115200n8 loglevel=4"
# Set boot parameters
LB_BOOTAPPEND_INSTALL=""
@@ -71,16 +71,16 @@ LB_HDD_SIZE="auto"
LB_HDD_PARTITION_START=""
# Set iso author
LB_ISO_APPLICATION="Debian Live"
LB_ISO_APPLICATION="n-OS-tr"
# Set iso preparer
LB_ISO_PREPARER="live-build @LB_VERSION@; https://salsa.debian.org/live-team/live-build"
# Set iso publisher
LB_ISO_PUBLISHER="Debian Live project; https://wiki.debian.org/DebianLive; debian-live@lists.debian.org"
LB_ISO_PUBLISHER="n-OS-tr project"
# Set iso volume (max 32 chars)
LB_ISO_VOLUME="Debian stable @ISOVOLUME_TS@"
LB_ISO_VOLUME="n-OS-tr"
# Set jffs2 eraseblock size
LB_JFFS2_ERASEBLOCK=""

View File

@@ -4,7 +4,7 @@
LB_ARCHITECTURE="amd64"
# Select distribution to use
LB_DISTRIBUTION="stable"
LB_DISTRIBUTION="bookworm"
# Select parent distribution to use
LB_PARENT_DISTRIBUTION=""
@@ -22,10 +22,10 @@ LB_DISTRIBUTION_BINARY="stable"
LB_PARENT_DISTRIBUTION_BINARY="stable"
# Select parent distribution for debian-installer to use
LB_PARENT_DEBIAN_INSTALLER_DISTRIBUTION=""
LB_PARENT_DEBIAN_INSTALLER_DISTRIBUTION="stable"
# Select archive areas to use
LB_ARCHIVE_AREAS="main"
LB_ARCHIVE_AREAS="main contrib non-free non-free-firmware"
# Select parent archive areas to use
LB_PARENT_ARCHIVE_AREAS="main"

View File

@@ -16,7 +16,7 @@ LB_APT_PIPELINE=""
LB_APT_RECOMMENDS="true"
# Set apt/aptitude security
LB_APT_SECURE="true"
LB_APT_SECURE="false"
# Set apt/aptitude source entries in sources.list
LB_APT_SOURCE_ARCHIVES="true"

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/live/0010-disable-kexec-tools.hook.chroot

View File

@@ -0,0 +1,44 @@
#!/bin/sh
# Install Slice B/C artifacts into the chroot before service enablement.
set -e
ART=/live-artifacts
if [ ! -d "$ART" ]; then
echo "[fetch-artifacts] no $ART directory; skipping"
exit 0
fi
# --- c-relay ---------------------------------------------------------------
if [ -f "$ART/c_relay_x86" ]; then
echo "[fetch-artifacts] installing c-relay binary"
if ! getent passwd c-relay >/dev/null 2>&1; then
adduser --system --no-create-home --group --home /opt/c-relay c-relay
fi
# --- nostr-id-tui -----------------------------------------------------------
if [ -f "$ART/nostr-id-tui_x86_64" ]; then
echo "[fetch-artifacts] installing nostr-id-tui binary"
install -m 0755 -o root -g root \
"$ART/nostr-id-tui_x86_64" /usr/local/sbin/nostr-id-tui
else
echo "[fetch-artifacts] WARNING: $ART/nostr-id-tui_x86_64 not found" >&2
fi
mkdir -p /opt/c-relay
install -m 0755 -o c-relay -g c-relay \
"$ART/c_relay_x86" /opt/c-relay/c_relay_x86
chown -R c-relay:c-relay /opt/c-relay
else
echo "[fetch-artifacts] WARNING: $ART/c_relay_x86 not found" >&2
fi
# --- fips ------------------------------------------------------------------
FIPS_DEB=$(ls "$ART"/fips_*_amd64.deb 2>/dev/null | head -1 || true)
if [ -n "$FIPS_DEB" ] && [ -f "$FIPS_DEB" ]; then
echo "[fetch-artifacts] installing fips package $FIPS_DEB"
# Non-interactive: keep current /etc/fips/fips.yaml (provided by includes.chroot)
DEBIAN_FRONTEND=noninteractive dpkg -i --force-confold "$FIPS_DEB"
else
echo "[fetch-artifacts] WARNING: no fips_*_amd64.deb found under $ART" >&2
fi

View File

@@ -0,0 +1,26 @@
#!/bin/sh
# Enable n-OS-tr services so they start automatically on boot.
set -e
# Disable the Debian default site so it doesn't conflict with ours.
rm -f /etc/nginx/sites-enabled/default
# Enable our site.
ln -sf /etc/nginx/sites-available/n-os-tr.conf /etc/nginx/sites-enabled/n-os-tr.conf
systemctl enable nginx.service
# Slice A
systemctl enable ginxsom.service
systemctl enable n-os-tr-firstboot.service
# Slice B
systemctl enable fips.service || true
systemctl enable fips-dns.service || true
# Slice C
systemctl enable c-relay.service
# TUI on tty1
systemctl disable getty@tty1.service || true
systemctl enable nostr-id-tui.service

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/live/0050-disable-sysvinit-tmpfs.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/1000-create-mtab-symlink.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/1010-enable-cryptsetup.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/1020-create-locales-files.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/5000-update-apt-file-cache.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/5010-update-apt-xapian-index.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/5020-update-glx-alternative.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/5030-update-plocate-database.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/5040-update-nvidia-alternative.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/5050-dracut.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8000-remove-adjtime-configuration.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8010-remove-backup-files.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8020-remove-dbus-machine-id.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8030-truncate-log-files.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8040-remove-mdadm-configuration.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8050-remove-openssh-server-host-keys.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8060-remove-systemd-machine-id.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8070-remove-temporary-files.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8080-reproducible-glibc.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8090-remove-ssl-cert-snakeoil.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8100-remove-udev-persistent-cd-rules.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/8110-remove-udev-persistent-net-rules.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/9000-remove-gnome-icon-cache.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/9010-remove-python-pyc.hook.chroot

View File

@@ -0,0 +1 @@
/home/user/lt/n_os_tr/live-build/share/hooks/normal/9020-remove-man-cache.hook.chroot

View File

@@ -0,0 +1,38 @@
# n-OS-tr fips configuration.
# Ephemeral identity by default (new keypair each boot).
node:
identity:
# Keep empty for ephemeral identity.
tun:
enabled: true
name: fips0
mtu: 1280
dns:
enabled: true
port: 5354
transports:
udp:
bind_addr: "0.0.0.0:2121"
tcp:
bind_addr: "0.0.0.0:8443"
ethernet:
# Rewritten on first boot by n-os-tr-firstboot to the first non-loopback iface.
interface: "__FIPS_ETH_IFACE__"
discovery: true
announce: true
auto_connect: true
accept_connections: true
peers:
- npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98"
alias: "fips-test-node"
addresses:
- transport: udp
addr: "217.77.8.91:2121"
connect_policy: auto_connect

View File

@@ -0,0 +1,24 @@
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;

View File

@@ -0,0 +1,95 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
image/png png;
image/webp webp;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/avif avif;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
application/vnd.wap.wmlc wmlc;
application/wasm wasm;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

View File

@@ -0,0 +1,83 @@
# n-OS-tr nginx site
# Terminates TLS, serves blobs directly from disk, forwards Blossom
# authenticated operations to ginxsom over FastCGI.
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# Redirect plain HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
http2 on;
server_name _;
# Self-signed cert generated on first boot
ssl_certificate /etc/ssl/n-os-tr/fullchain.pem;
ssl_certificate_key /etc/ssl/n-os-tr/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Uploads can be large
client_max_body_size 100m;
# Blobs root — nginx serves GET directly from disk
root /var/www/html;
index index.html;
# 1) Landing page / any static docs
location = / {
try_files /index.html =404;
}
# 2) Blob retrieval: GET /<64-hex sha256>
# First try the file on disk; if absent, fall back to ginxsom
# (which may have metadata but no bytes, or may need to 404 with
# a proper Blossom error).
location ~ "^/(?<blobhash>[a-f0-9]{64})$" {
root /var/www/blobs;
try_files /$blobhash @ginxsom;
}
# 3) Blossom authenticated operations go to ginxsom over FastCGI
location = /upload { try_files $uri @ginxsom; }
location = /mirror { try_files $uri @ginxsom; }
location = /report { try_files $uri @ginxsom; }
location /list/ { try_files $uri @ginxsom; }
location /blossom-admin/ { try_files $uri @ginxsom; }
# 4) c-relay websocket endpoint: wss://host/relay -> ws://127.0.0.1:8888/
location = /relay {
proxy_pass http://127.0.0.1:8888/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
# 5) c-relay admin API/UI
location /admin/ {
proxy_pass http://127.0.0.1:8888/api/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location @ginxsom {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/bin/ginxsom/ginxsom-fcgi;
fastcgi_pass unix:/run/ginxsom/ginxsom-fcgi.sock;
fastcgi_read_timeout 300s;
}
}

View File

@@ -0,0 +1,34 @@
[Unit]
Description=C Nostr Relay (n-OS-tr slice C)
Documentation=file:///usr/share/doc/n-os-tr/README
After=network.target n-os-tr-firstboot.service
Wants=network-online.target
[Service]
Type=simple
User=c-relay
Group=c-relay
WorkingDirectory=/opt/c-relay
Environment=DEBUG_LEVEL=0
ExecStart=/opt/c-relay/c_relay_x86 --debug-level=0
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=c-relay
# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/c-relay
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6
LimitNOFILE=65536
LimitNPROC=4096
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,3 @@
[Unit]
Wants=n-os-tr-firstboot.service
After=n-os-tr-firstboot.service

View File

@@ -0,0 +1,3 @@
[Unit]
# If the TUI binary exists, do not start tty1 getty so tty1 is reserved for nostr-id-tui.
ConditionPathExists=!/usr/local/sbin/nostr-id-tui

View File

@@ -0,0 +1,33 @@
[Unit]
Description=Ginxsom Blossom Server
Documentation=https://github.com/hzrd149/blossom
After=network.target n-os-tr-firstboot.service
Requires=n-os-tr-firstboot.service
ConditionPathExists=/var/lib/n-os-tr/env
[Service]
Type=forking
User=www-data
Group=www-data
# Provides /run/ginxsom/, cleaned up on service stop
RuntimeDirectory=ginxsom
RuntimeDirectoryMode=0755
EnvironmentFile=/var/lib/n-os-tr/env
ExecStartPre=/bin/rm -f /run/ginxsom/ginxsom-fcgi.sock
ExecStart=/usr/bin/spawn-fcgi \
-s /run/ginxsom/ginxsom-fcgi.sock \
-M 0660 \
-u www-data \
-g www-data \
-d /var/lib/ginxsom \
-- /usr/local/bin/ginxsom/ginxsom-fcgi \
--admin-pubkey ${GINXSOM_ADMIN_PUBKEY} \
--server-privkey ${GINXSOM_SERVER_PRIVKEY} \
--db-path /var/lib/ginxsom \
--storage-dir /var/www/blobs
ExecStop=/usr/bin/pkill -f ginxsom-fcgi
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,16 @@
[Unit]
Description=n-OS-tr first-boot key and TLS provisioning
Documentation=file:///usr/share/doc/n-os-tr/README
ConditionPathExists=!/var/lib/n-os-tr/.provisioned
Before=ginxsom.service nginx.service
Wants=ginxsom.service nginx.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/n-os-tr-firstboot
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,3 @@
[Unit]
Wants=n-os-tr-firstboot.service
After=n-os-tr-firstboot.service

View File

@@ -0,0 +1,23 @@
[Unit]
Description=n-OS-tr Nostr Identity TUI
Documentation=file:///usr/share/doc/n-os-tr/README
After=systemd-user-sessions.service network-online.target n-os-tr-firstboot.service
Wants=network-online.target n-os-tr-firstboot.service
Conflicts=getty@tty1.service
Before=getty@tty1.service
[Service]
Type=simple
ExecStart=/usr/local/sbin/nostr-id-tui
Restart=always
RestartSec=1
StandardInput=tty
StandardOutput=tty
StandardError=journal
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,140 @@
#!/bin/bash
# n-os-tr-smoketest — health check for n-OS-tr services.
# Extend with new sections as slices B (fips) and C (c-relay) land.
set -u
# -------------------- output helpers --------------------
if [[ -t 1 ]] && [[ "${NO_COLOR:-}" != "1" ]]; then
C_PASS=$'\e[32m'
C_FAIL=$'\e[31m'
C_SKIP=$'\e[33m'
C_HEAD=$'\e[1;36m'
C_RST=$'\e[0m'
else
C_PASS=''
C_FAIL=''
C_SKIP=''
C_HEAD=''
C_RST=''
fi
declare -i PASS=0 FAIL=0 SKIP=0
pass() { printf " %s[PASS]%s %-45s (%s)\n" "$C_PASS" "$C_RST" "$1" "${2:-ok}"; PASS+=1; }
fail() { printf " %s[FAIL]%s %-45s (%s)\n" "$C_FAIL" "$C_RST" "$1" "${2:-failed}"; FAIL+=1; }
skip() { printf " %s[SKIP]%s %-45s (%s)\n" "$C_SKIP" "$C_RST" "$1" "${2:-skipped}"; SKIP+=1; }
section() { printf "\n%s== %s ==%s\n" "$C_HEAD" "$1" "$C_RST"; }
# a convenience wrapper: runs a test command, passes if exit 0
# usage: check "label" command args...
check() {
local label="$1"
shift
if "$@" >/dev/null 2>&1; then
pass "$label" "$*"
else
local rc=$?
fail "$label" "$* -> exit $rc"
fi
}
# -------------------- core section ----------------------
section "core"
check "n-os-tr-firstboot active" systemctl is-active n-os-tr-firstboot.service
check "n-os-tr-firstboot succeeded" \
bash -c '[[ "$(systemctl show -p Result --value n-os-tr-firstboot.service)" == "success" ]]'
check "firstboot marker exists" test -f /var/lib/n-os-tr/.provisioned
check "ginxsom nsec exists" test -f /var/lib/n-os-tr/keys/ginxsom.nsec
check "env file readable by www-data" \
sudo -u www-data test -r /var/lib/n-os-tr/env
# -------------------- nginx + ginxsom --------------------
section "blossom (nginx + ginxsom)"
check "nginx active" systemctl is-active nginx.service
check "ginxsom active" systemctl is-active ginxsom.service
check "ginxsom socket exists" test -S /run/ginxsom/ginxsom-fcgi.sock
# HTTPS landing page
status=$(curl -sk -o /dev/null -w '%{http_code}' https://localhost/ 2>/dev/null)
if [[ "$status" == "200" ]]; then
pass "landing page over TLS" "HTTP $status"
else
fail "landing page over TLS" "HTTP $status"
fi
# BUD-01: HEAD on a random 64-hex should not explode (404 is fine, 5xx is not)
status=$(curl -sk -o /dev/null -w '%{http_code}' -I \
https://localhost/0000000000000000000000000000000000000000000000000000000000000000 2>/dev/null)
case "$status" in
200|404) pass "blob HEAD returns a non-5xx" "HTTP $status" ;;
*) fail "blob HEAD returns a non-5xx" "HTTP $status" ;;
esac
# -------------------- fips mesh daemon --------------------
section "fips mesh daemon"
check "fips.service active" systemctl is-active fips.service
check "fips-dns.service active" systemctl is-active fips-dns.service
check "fips0 interface present" ip link show fips0
npub=$(fipsctl show status 2>/dev/null | jq -r '.identity.npub // .npub // empty' | head -1 | tr -d '[:space:]')
case "$npub" in
npub1*) pass "fipsctl status returns npub" "${npub:0:20}..." ;;
*) fail "fipsctl status returns npub" "got: ${npub:-<empty>}" ;;
esac
check "fips UDP bound on :2121" bash -c 'ss -ulnp 2>/dev/null | grep -q ":2121"'
# -------------------- c-relay nostr relay -----------------
section "c-relay nostr relay"
check "c-relay.service active" systemctl is-active c-relay.service
check "c-relay listening on :8888" bash -c 'ss -tlnp 2>/dev/null | grep -q ":8888"'
if journalctl -u c-relay.service --no-pager 2>/dev/null | grep -q "Admin Private Key:"; then
pass "admin nsec logged in journal" "found"
elif ls /opt/c-relay/*.db >/dev/null 2>&1; then
pass "admin nsec logged in journal" "relay initialized (db present)"
else
fail "admin nsec logged in journal" "journal miss and no relay db"
fi
ws_status=$(curl -sk --http1.1 -o /dev/null -w '%{http_code}' \
-H 'Connection: Upgrade' \
-H 'Upgrade: websocket' \
-H 'Sec-WebSocket-Version: 13' \
-H 'Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==' \
https://localhost/relay 2>/dev/null)
if [[ "$ws_status" == "101" ]]; then
pass "wss://localhost/relay websocket upgrade" "HTTP $ws_status"
else
fail "wss://localhost/relay websocket upgrade" "HTTP $ws_status"
fi
nip11=$(curl -sk -H 'Accept: application/nostr+json' https://localhost/relay 2>/dev/null)
if echo "$nip11" | jq -e '.name' >/dev/null 2>&1; then
pass "NIP-11 info doc reachable" "name=$(echo "$nip11" | jq -r .name)"
else
fail "NIP-11 info doc reachable" "no JSON .name"
fi
status=$(curl -sk -o /dev/null -w '%{http_code}' https://localhost/admin/ 2>/dev/null)
case "$status" in
200|302|401) pass "admin UI reachable via nginx" "HTTP $status" ;;
*) fail "admin UI reachable via nginx" "HTTP $status" ;;
esac
# -------------------- summary ---------------------------
section "summary"
printf " %s%d PASS%s %s%d FAIL%s %s%d SKIP%s\n" \
"$C_PASS" "$PASS" "$C_RST" \
"$C_FAIL" "$FAIL" "$C_RST" \
"$C_SKIP" "$SKIP" "$C_RST"
if [[ $FAIL -gt 0 ]]; then
exit 1
fi
exit 0

Binary file not shown.

View File

@@ -0,0 +1,130 @@
#!/bin/bash
# n-OS-tr first-boot provisioning.
# Runs once per boot-without-persistence. Generates per-node secrets
# and writes them to /var/lib/n-os-tr/ where the service units read them.
set -euo pipefail
STATE=/var/lib/n-os-tr
KEYS=$STATE/keys
ENVFILE=$STATE/env
TLS_DIR=/etc/ssl/n-os-tr
BLOBS=/var/www/blobs
GINXSOM_DB=/var/lib/ginxsom
log() { echo "[firstboot] $*"; }
mkdir -p "$STATE" "$KEYS" "$TLS_DIR" "$BLOBS" "$GINXSOM_DB"
chmod 0700 "$KEYS"
chmod 0755 "$BLOBS" "$GINXSOM_DB"
# ---------------------------------------------------------------------------
# 1. Ginxsom server keypair.
# We use nak (pre-shipped at /usr/local/bin/nak) to generate and decode.
# ---------------------------------------------------------------------------
if [[ ! -f "$KEYS/ginxsom.nsec" ]]; then
log "Generating ginxsom server keypair"
/usr/local/bin/nak key generate > "$KEYS/ginxsom.nsec"
chmod 0400 "$KEYS/ginxsom.nsec"
fi
GINXSOM_SECRET=$(tr -d '[:space:]' < "$KEYS/ginxsom.nsec")
# nak key generate currently emits raw hex, but we also accept bech32 nsec.
case "$GINXSOM_SECRET" in
nsec1*)
GINXSOM_SERVER_PRIVKEY=$(/usr/local/bin/nak decode "$GINXSOM_SECRET" | jq -r .private_key)
;;
*)
GINXSOM_SERVER_PRIVKEY="$GINXSOM_SECRET"
;;
esac
GINXSOM_SERVER_PUBKEY=$(/usr/local/bin/nak key public "$GINXSOM_SERVER_PRIVKEY" | tr -d '[:space:]')
# The admin pubkey for this build is the same as the server pubkey.
# This is a simplification for Slice A (every node is its own admin).
# Slice C (c-relay) will introduce a separate admin keypair.
GINXSOM_ADMIN_PUBKEY=$GINXSOM_SERVER_PUBKEY
log "ginxsom server pubkey (hex): $GINXSOM_SERVER_PUBKEY"
# ---------------------------------------------------------------------------
# 2. Self-signed TLS cert.
# ---------------------------------------------------------------------------
if [[ ! -f "$TLS_DIR/fullchain.pem" ]]; then
log "Generating self-signed TLS certificate"
HN=$(hostname)
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
-subj "/CN=$HN" \
-addext "subjectAltName=DNS:$HN,DNS:localhost,IP:127.0.0.1,IP:::1" \
-keyout "$TLS_DIR/privkey.pem" \
-out "$TLS_DIR/fullchain.pem" >/dev/null 2>&1
chmod 0640 "$TLS_DIR/privkey.pem"
chmod 0644 "$TLS_DIR/fullchain.pem"
chown root:www-data "$TLS_DIR/privkey.pem"
fi
# ---------------------------------------------------------------------------
# 3. Write environment file consumed by ginxsom.service.
# ---------------------------------------------------------------------------
log "Writing $ENVFILE"
umask 077
cat > "$ENVFILE" <<EOKEY
GINXSOM_SERVER_PRIVKEY=$GINXSOM_SERVER_PRIVKEY
GINXSOM_ADMIN_PUBKEY=$GINXSOM_ADMIN_PUBKEY
EOKEY
chmod 0440 "$ENVFILE"
chown root:www-data "$ENVFILE"
# ---------------------------------------------------------------------------
# 4. Configure fips ethernet interface (required by current fips schema).
# ---------------------------------------------------------------------------
FIPS_CFG=/etc/fips/fips.yaml
if [[ -f "$FIPS_CFG" ]] && grep -q "__FIPS_ETH_IFACE__" "$FIPS_CFG"; then
FIPS_ETH_IFACE=$(ip -o link show | awk -F': ' '$2 != "lo" {name=$2; sub(/@.*/, "", name); print name; exit}')
if [[ -z "${FIPS_ETH_IFACE:-}" ]]; then
log "No non-loopback network interface found for fips ethernet transport"
exit 1
fi
log "Configuring fips ethernet interface: $FIPS_ETH_IFACE"
sed -i "s/__FIPS_ETH_IFACE__/$FIPS_ETH_IFACE/g" "$FIPS_CFG"
fi
# ---------------------------------------------------------------------------
# 5. Ownership of runtime directories ginxsom will write to.
# ---------------------------------------------------------------------------
chown -R www-data:www-data "$BLOBS" "$GINXSOM_DB"
# ---------------------------------------------------------------------------
# 6. Update the MOTD with the generated identity so the user can find it.
# ---------------------------------------------------------------------------
cat > /etc/motd <<EOMOTD
n-OS-tr — Nostr-addressed mesh node (preview)
Blossom (ginxsom) server pubkey (hex):
$GINXSOM_SERVER_PUBKEY
retrieve nsec: cat /var/lib/n-os-tr/keys/ginxsom.nsec
c-relay Nostr relay:
wss://$(hostname)/relay (admin nsec: journalctl -u c-relay | grep 'Admin Private Key')
https://$(hostname)/admin/ (web admin/API)
fips mesh daemon:
fipsctl identity (show npub)
fipsctl peers (list peers)
Service status: systemctl status fips-dns ginxsom c-relay nginx n-os-tr-firstboot
Quick health check: n-os-tr-smoketest
Identity is ephemeral unless you use a persistence partition or
install to disk. See /usr/share/doc/n-os-tr/README for details.
EOMOTD
# ---------------------------------------------------------------------------
# 7. Marker so systemd's ConditionPathExists= skips us on subsequent boots.
# ---------------------------------------------------------------------------
touch "$STATE/.provisioned"
log "First-boot provisioning complete."

View File

@@ -0,0 +1,36 @@
n-OS-tr live ISO (Phase 2 Slice B+C)
===================================
This live image enables:
- n-os-tr-firstboot.service (oneshot provisioning)
- ginxsom.service (Blossom FastCGI server)
- nginx.service (TLS frontend)
- fips.service + fips-dns.service (mesh daemon + DNS responder)
- c-relay.service (Nostr relay)
First boot generates:
- ginxsom identity material under /var/lib/n-os-tr/keys/
- service environment at /var/lib/n-os-tr/env
- self-signed TLS certs under /etc/ssl/n-os-tr/
Useful checks:
- n-os-tr-smoketest
- Output shape: [PASS|FAIL|SKIP] <label> (<detail>) plus a PASS/FAIL/SKIP summary.
- systemctl status n-os-tr-firstboot ginxsom nginx fips fips-dns c-relay
- journalctl -u n-os-tr-firstboot.service
- journalctl -u c-relay.service | grep 'Admin Private Key:'
- fipsctl identity
- fipsctl peers
- cat /var/lib/n-os-tr/keys/ginxsom.nsec
- curl -k https://localhost/
- curl -k -H 'Accept: application/nostr+json' https://localhost/relay
Relay endpoints:
- wss://<host>/relay
- https://<host>/admin/
If terminal colours render poorly, run: NO_COLOR=1 n-os-tr-smoketest
Notes:
- Identity is ephemeral by default for this slice (ginxsom nsec, fips identity, c-relay admin key).
- fips transport is configured for ethernet discovery and UDP bootstrap peer connectivity.

View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>n-OS-tr</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 680px; margin: 4rem auto; padding: 0 1rem; color: #ddd; background: #111; }
h1 { font-weight: 300; letter-spacing: 0.05em; }
code { background: #222; padding: 0.15rem 0.35rem; border-radius: 3px; }
.services li { margin-bottom: 0.5rem; }
a { color: #8cf; }
</style>
</head>
<body>
<h1>n-OS-tr</h1>
<p>A Nostr-addressed mesh node. Early preview.</p>
<h2>Services</h2>
<ul class="services">
<li><strong>Blossom server (ginxsom):</strong> Blossom blob endpoints under <code>/</code> — upload at <code>PUT /upload</code>, retrieve at <code>GET /<sha256></code>.</li>
<li><strong>Nostr relay (c-relay):</strong> <em>not yet enabled in this build</em>.</li>
<li><strong>FIPS mesh daemon:</strong> <em>not yet enabled in this build</em>.</li>
</ul>
<p>This node's identity and keys were generated on first boot. See <code>/etc/motd</code> when you log in.</p>
</body>
</html>

View File

@@ -0,0 +1,17 @@
nginx-light
spawn-fcgi
libfcgi-bin
ca-certificates
openssl
curl
jq
vim
htop
# Slice B/C runtime dependencies
libdbus-1-3
iproute2
nftables
# TUI runtime dependency
libncursesw6

View File

@@ -1,11 +1,4 @@
#!/bin/bash
# Live-build wrapper script
# This script allows you to run live-build commands from the local repository
# without needing to type the full path each time
# Set the LIVE_BUILD environment variable to point to our local repository
export LIVE_BUILD="/home/teknari/Sync/Programming/VibeCoding/n_os_tr/live-build"
# Execute the lb command from our repository with all passed arguments
# Live-build wrapper — points at the vendored live-build repo
export LIVE_BUILD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/live-build"
exec "${LIVE_BUILD}/frontend/lb" "$@"

Submodule live-build updated: c2c3f77929...06b8643af9

361
plans/identity_subsystem.md Normal file
View File

@@ -0,0 +1,361 @@
# n-OS-tr Identity Subsystem
> **Status:** Design. This document describes how n-OS-tr turns a single 12-word BIP-39 mnemonic into every key the OS and its applications need, and how the **identity agent** exposes those keys to the rest of the system.
>
> Companion docs: [`plans/threat_model.md`](threat_model.md) for the privacy contract this must satisfy, and [`plans/nostr_config_projection.md`](nostr_config_projection.md) for how the derived main key is used to fetch configuration.
## 1. Concept
n-OS-tr has one piece of root secret material: **a 12-word BIP-39 mnemonic**. Every key the system needs — your public identity, your local relay admin key, your mesh node identity, your blob-store key — is deterministically derived from that mnemonic via [NIP-06](https://github.com/nostr-protocol/nips/blob/master/06.md).
The user enters (or generates) the mnemonic at boot. A single long-running process — the **identity-agent** — holds the mnemonic in locked memory for the session, derives keys on demand, and signs events on behalf of apps via [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md). No app and no file ever sees the mnemonic or a raw private key.
On shutdown, the identity-agent zeroes the memory, and the ephemeral rootfs disappears. The mnemonic only ever existed in RAM.
## 1.1 Crypto foundation: `nostr_core_lib`
The identity-agent does **not** reimplement any Nostr cryptography. It is built on top of [`includes/nostr_core_lib`](../includes/nostr_core_lib/), a C library already vendored into this repository that provides:
| Capability needed by identity-agent | `nostr_core_lib` symbol / header | NIP |
|--------------------------------------------|----------------------------------------------------------------------------------------|---------|
| Keypair generation (entropy source) | [`nostr_generate_keypair`](../includes/nostr_core_lib/nostr_core/nip006.h:20) | n/a |
| BIP-39 mnemonic generation + NIP-06 derivation | [`nostr_generate_mnemonic_and_keys`](../includes/nostr_core_lib/nostr_core/nip006.h:21) | NIP-06 |
| NIP-06 derivation from existing mnemonic | [`nostr_derive_keys_from_mnemonic`](../includes/nostr_core_lib/nostr_core/nip006.h:24) | NIP-06 |
| Input type detection (hex vs nsec vs mnemonic) | [`nostr_detect_input_type`](../includes/nostr_core_lib/nostr_core/nip006.h:26) | n/a |
| bech32 encoding/decoding (nsec/npub) | [`nostr_key_to_bech32`](../includes/nostr_core_lib/nostr_core/nostr_core.h), [`nostr_decode_nsec`](../includes/nostr_core_lib/nostr_core/nip006.h:27) | NIP-19 |
| Event creation + signing | [`nostr_create_and_sign_event`](../includes/nostr_core_lib/nostr_core/nostr_core.h) | NIP-01 |
| NIP-44 encrypt / decrypt (for self-encrypted config) | [`nostr_nip44_encrypt`](../includes/nostr_core_lib/nostr_core/nip044.h:28), [`nostr_nip44_decrypt`](../includes/nostr_core_lib/nostr_core/nip044.h:62) | NIP-44 |
| NIP-46 signer session (serve requests) | [`nostr_nip46_signer_session_init`](../includes/nostr_core_lib/nostr_core/nip046.h:163), [`nostr_nip46_signer_handle_request`](../includes/nostr_core_lib/nostr_core/nip046.h:169) | NIP-46 |
| NIP-46 bunker URL publishing (external signer inbound path) | [`nostr_nip46_signer_create_bunker_url`](../includes/nostr_core_lib/nostr_core/nip046.h:172) | NIP-46 |
| NIP-46 client session (consume remote bunker) | [`nostr_nip46_client_session_init`](../includes/nostr_core_lib/nostr_core/nip046.h:145), [`nostr_nip46_client_sign_event`](../includes/nostr_core_lib/nostr_core/nip046.h:158) | NIP-46 |
| Relay pool + NIP-42 auth for signer relay | [`nostr_relay_pool_create`](../includes/nostr_core_lib/nostr_core/nostr_core.h), [`nostr_relay_pool_set_auth`](../includes/nostr_core_lib/nostr_core/nostr_core.h) | NIP-42 |
| Library-wide log callback (no on-disk logs) | [`nostr_set_log_callback`](../includes/nostr_core_lib/nostr_core/nostr_log.h) | n/a |
Consequence: the identity-agent is a thin C daemon that wires these primitives to systemd, to a local Unix-domain transport, and to the privacy-hardener's memory and shutdown constraints. The crypto has been written, reviewed, and unit-tested in [`includes/nostr_core_lib/tests`](../includes/nostr_core_lib/tests/). We do not touch it.
The Rust- and Python-binding libraries mentioned in §5.2 are thin FFI wrappers over the same library.
## 2. Derivation scheme
n-OS-tr uses the exact NIP-06 derivation exposed by `nostr_core_lib` via [`nostr_derive_keys_from_mnemonic(mnemonic, account, priv, pub)`](../includes/nostr_core_lib/nostr_core/nip006.h:24), where `account` is the role index. This matches both the nostr-tools JavaScript reference implementation in [`/home/user/lt/client/www/tools.html`](../../client/www/tools.html) (line 944) and the standard [NIP-06 specification](https://github.com/nostr-protocol/nips/blob/master/06.md).
The derivation path is:
```
m / 44' / 1237' / index' / 0 / 0
```
where:
- `44'` — BIP-44 purpose
- `1237'` — SLIP-0044 coin type for Nostr
- `index'` — the role index (see table below)
- `0 / 0` — change and address, always zero
Given a 12-word mnemonic, each integer `index` produces a distinct 32-byte secp256k1 private key, from which Nostr npub/nsec are encoded.
### 2.1 The role table (schedule)
Indices are assigned to fixed roles. Index 0 is stable forever; higher indices are allocated as roles are needed. New apps request a new index at *install time*, not at runtime, so the schedule is discoverable and forward-compatible.
| Index | Role name | Used by | Stability |
|-------|----------------|-------------------------------------------------------------------------|------------|
| `0` | `main` | User's public Nostr identity. Profile, posts, social graph, config sig. | Immutable. |
| `1` | `throwaway` | Test/experimental account. Safe to publish noise from. | Stable. |
| `2` | `c-relay-admin`| Admin key for the user's local [`c-relay`](../includes/c-relay/). | Stable. |
| `3` | `ginxsom` | Server key for the user's local [`ginxsom`](../includes/ginxsom/). | Stable. |
| `4` | `fips` | Mesh node identity for [`fips`](../includes/fips/). Yields fips IPv6. | Stable. |
| `5` | reserved | Future: personal Nostr DM relay / gossip cache. | Reserved. |
| `6` | reserved | Future: signer key for system audit events. | Reserved. |
| `7+` | unallocated | Assigned by future apps via the registration process in §2.3. | Dynamic. |
> **Index 0 stability:** Once a user has published anything under their main key, index 0 can never be renumbered. That's why `main` gets index 0 forever. All other role assignments can in principle be renumbered before first use, but once shipped in a released n-OS-tr image they must not be changed without a major version bump.
### 2.2 Deriving a public identifier from a key
Once a private key is derived at an index, standard Nostr encodings apply. In `nostr_core_lib` these are all available directly:
| Want | `nostr_core_lib` call |
|---------------------------|----------------------------------------------------------------------------------------|
| `npub` (bech32 pubkey) | `nostr_key_to_bech32(public_key, "npub", out)` |
| `nsec` (bech32 privkey) | `nostr_key_to_bech32(private_key, "nsec", out)` |
| `hex` pubkey | produced directly by [`nostr_derive_keys_from_mnemonic`](../includes/nostr_core_lib/nostr_core/nip006.h:24) as 32 bytes |
| `hex` privkey | produced directly by [`nostr_derive_keys_from_mnemonic`](../includes/nostr_core_lib/nostr_core/nip006.h:24) as 32 bytes |
| FIPS mesh IPv6 | `fd` + first 15 bytes of `sha256(pubkey_bytes)` — implemented locally in identity-agent; the definition matches [`tools.html`](../../client/www/tools.html:750) `pubkeyHexToFipsIpv6()` |
No identity-agent code ever touches raw secp256k1 arithmetic. Everything goes through the library.
### 2.3 Role registration protocol
When a new app wants its own Nostr identity, it does not pick an index itself. It calls the identity-agent:
```
identity-agent register --service=my-app --justification="needs a Nostr key for X"
```
The agent:
1. Looks up the currently allocated schedule (see §4).
2. Allocates the next unused index.
3. Returns the npub (never the nsec or the mnemonic).
4. Persists the name → index mapping **as a signed Nostr config event** under the main key (kind described in [`plans/nostr_config_projection.md`](nostr_config_projection.md)), so the mapping is itself part of the user's portable config.
Because the mapping is on Nostr, it travels with the user, not with the machine. Booting on a different n-OS-tr machine reconstructs the same role table.
## 3. Boot-time UX
When n-OS-tr boots, before any network or user session, it runs the identity-agent as a systemd service, which presents the first-screen flow on whatever console the device has (TTY, framebuffer, LCD HAT).
### 3.1 Three flows
```mermaid
flowchart TB
A[Boot] --> Q{Identity source?}
Q -->|Enter mnemonic| E[Enter 12 words]
Q -->|Generate new| G[Generate 12 words]
Q -->|Import file| F[Import from /media/*/mnemonic.txt]
Q -->|Connect bunker| B[NIP-46 bunker URL]
E --> V[BIP-39 checksum validate]
G --> D[Display once, require confirmation]
F --> W[Read once, wipe source file]
V --> L[Load mnemonic into locked memory]
D --> L
W --> L
B --> RS[Use remote signer,<br/>no local mnemonic]
L --> R[Ready: expose keys + signer]
RS --> R
```
### 3.2 Flow details
**Flow 1 — "I have a mnemonic, let me type it."**
- 12 words entered via keyboard (amd64 TTY) or Waveshare-LCD button sequence (Pi Zero).
- Each word is validated against the BIP-39 English wordlist as typed. Typos are rejected before the full phrase is submitted.
- Checksum validated on submit. If invalid, clear the buffer and prompt again.
- On success, loaded into `mlock()`-ed memory.
**Flow 2 — "Generate a new one for me."**
- Entropy sourced from `getrandom(2)` + hardware RNG where available.
- 12 words are displayed *once*, full-screen, with an emphatic "write these down now" message.
- User confirms by retyping (or on Pi: scrolling and clicking through) 3 random word positions (e.g., word 4, word 9, word 11) to ensure they actually recorded the phrase.
- Only after confirmation does boot proceed. If the user reboots without confirming, they have generated nothing of value — no identity has been "committed to" yet.
**Flow 3 — "Import from removable media."**
- At boot, identity-agent scans `/media/*/mnemonic.txt` for a single candidate file.
- Reads it, validates BIP-39 checksum.
- **Wipes the source file** (`shred -u` on ext4 or equivalent on FAT) so that unplugging the stick does not carry the plaintext mnemonic off the machine. User is informed that the stick no longer contains the mnemonic and they must have a backup.
- Loaded into locked memory.
**Flow 4 — "Use an external signer."**
- User provides a [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md) bunker URL (hardware wallet, phone app, remote machine).
- identity-agent does not hold any mnemonic. All signing is forwarded to the external bunker.
- Key derivation for non-signing use (e.g., deriving a fips IPv6) proxies through the bunker's `nip04`/`nip44` primitives where possible. Roles that *require* a raw private key locally (e.g., running a local c-relay that stores its own nsec) are disabled or limited in this mode.
### 3.3 Per-flavor UX
| Image flavor | Mnemonic entry UX |
|------------------------------------|----------------------------------------------------------------------|
| **amd64 live ISO (text console)** | Full-screen TUI over `/dev/tty1`, keyboard entry, word auto-complete |
| **amd64 live ISO with GUI** | Boot-time TUI same as above; the GUI is launched *after* identity is loaded, never before |
| **Raspberry Pi Zero 2 W** | Waveshare 1.3" LCD HAT with button navigation: wheel to scroll wordlist, press to select; joystick for "next word"; confirmation via button combo. See [`plans/keyboard_gpio_matrix.md`](../../raspberry_pi_zero_nostr/plans/keyboard_gpio_matrix.md). |
| **Headless (SSH) setup** | Not supported by default. Mnemonic entry MUST be on the physical console to avoid keystroke exfil over the network. A future opt-in "paste via serial" path is possible but out of scope for now. |
## 4. The identity-agent daemon
A single long-running process is the authority for all identity operations. It is the only process that ever sees the mnemonic.
### 4.1 Responsibilities
1. Present the boot-time mnemonic UX (or delegate to a TUI binary it supervises).
2. Hold the mnemonic + derived keys in `mlock()`-ed, non-swappable memory.
3. Derive keys on demand by index.
4. Act as a [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md) signer for apps (local, over Unix socket; optionally over fips mesh for mesh-local apps).
5. Track the role→index schedule; register new roles (§2.3).
6. On shutdown or SIGTERM, zero all key material before exit.
### 4.2 Non-responsibilities
- It does **not** publish events itself. It signs, apps publish.
- It does **not** manage relays. That's app-level.
- It does **not** touch config projection. That's the config-loader/writer (see [`plans/nostr_config_projection.md`](nostr_config_projection.md)). It only *signs* events on behalf of the config-writer.
- It does **not** store the mnemonic anywhere but RAM. No `~/.nostr`, no `/etc/n-os-tr/identity`, nothing.
### 4.3 Processes and isolation
```mermaid
flowchart LR
User[User at console] -- 12 words --> UX[identity-agent-ui<br/>TUI or LCD]
UX -- passes via pipe --> Agent[identity-agent<br/>daemon]
App1[c-relay] -- NIP-46 --> Signer[local signer socket<br/>/run/nostr-id/signer.sock]
App2[ginxsom] -- NIP-46 --> Signer
App3[fips] -- NIP-46 --> Signer
App4[config-loader] -- NIP-46 --> Signer
Signer --- Agent
subgraph mem[mlock-ed, non-swap memory]
Agent
end
```
The agent runs as its own user (e.g., `nostr-id`), with:
- `MemoryDenyWriteExecute=yes`
- `LockPersonality=yes`
- `NoNewPrivileges=yes`
- `ProtectSystem=strict`
- `ProtectHome=yes`
- `PrivateTmp=yes`
- `RestrictAddressFamilies=AF_UNIX AF_NETLINK`
- `SystemCallArchitectures=native`
- `SystemCallFilter=@system-service`
- `CapabilityBoundingSet=CAP_IPC_LOCK` (for `mlock`), nothing else.
The signer Unix socket is accessible only to a well-defined group (`nostr-signer`). Apps that want to sign add their service user to that group.
### 4.4 Deriving without revealing
Most apps do NOT need the raw private key. They need signed events. For them, the NIP-46 flow is:
```mermaid
sequenceDiagram
autonumber
participant App as c-relay
participant Sig as signer socket
participant Ag as identity-agent
App->>Sig: connect(/run/nostr-id/signer.sock)
App->>Sig: NIP-46 "describe" (role=c-relay-admin)
Sig->>Ag: resolve role, returns npub
Ag-->>Sig: npub for role
Sig-->>App: npub for role
App->>Sig: NIP-46 "sign_event" (kind=N, tags=..., content=...)
Sig->>Ag: derive sk for role, sign, zero sk
Ag-->>Sig: signed event JSON
Sig-->>App: signed event JSON
```
A small number of apps — those that can't use a remote signer (e.g., legacy tools) — can request the *raw nsec*. That call is gated:
- Only callable by specific whitelisted service users.
- Only for specific role indices.
- Rejected entirely when the user is operating in "external bunker" mode (§3.2 Flow 4).
## 5. Public interface
### 5.1 CLI: `nostr-id`
| Command | Description |
|----------------------------------|--------------------------------------------------------------------------|
| `nostr-id status` | Show whether the agent is running and how it was bootstrapped (entered / generated / imported / bunker). Never prints keys. |
| `nostr-id show npub <role>` | Print the npub for a role name. Uses the role→index table from config. |
| `nostr-id show hex <role>` | Print the hex pubkey for a role. |
| `nostr-id show fips-ipv6 <role>` | Print the FIPS mesh IPv6 for a role (usually `main`). |
| `nostr-id list` | Print the role table: name, index, npub. |
| `nostr-id register <service>` | Allocate a new role index for a service. Interactive confirmation. |
| `nostr-id derive <role>` | Print the raw nsec for a role. Gated (see §4.4); prompts for confirmation.|
| `nostr-id sign <file>` | Sign an unsigned event JSON from stdin/file for a role, print signed. |
| `nostr-id lock` | Zero the mnemonic from RAM *without* shutting down (panic button). |
| `nostr-id bunker-connect <url>` | Connect to an external NIP-46 bunker instead of a local mnemonic. |
The agent also exposes a DBus interface on the system bus under `org.n_os_tr.IdentityAgent` with matching methods, for GUI clients and other language bindings.
### 5.2 Library: `libnostr-id` (planned)
A small C library + Rust crate + Python binding that wraps the signer socket. Apps depend on this instead of implementing NIP-46 themselves.
### 5.3 Integration with current services
After Phase 3 lands, the services that exist today ([`fips`](../includes/fips/), [`c-relay`](../includes/c-relay/), [`ginxsom`](../includes/ginxsom/)) will drop their current "generate a fresh key on first boot" behavior and instead:
- On startup, call `nostr-id show npub <role>` + `nostr-id sign ...` (or `nostr-id derive <role>` for services that require raw keys).
- Their systemd units add `After=nostr-id.service` and `Requires=nostr-id.service`.
- The [`n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot) script becomes a much thinner shim: identity generation moves out of it entirely, into the agent.
## 6. Memory safety mechanics
Enforcing [F1F7 of the threat model](threat_model.md#41-the-mnemonic-and-derived-keys) requires deliberate engineering:
- **`mlock(2)`.** The agent locks pages containing mnemonic and derived keys so the kernel can't swap them. Requires `CAP_IPC_LOCK`.
- **Zeroize-on-drop.** Key material is wrapped in a type (Rust `zeroize::Zeroizing`, or C `volatile` + explicit_bzero) that guarantees the bytes are cleared when the owner is dropped.
- **No `core` dumps.** `prctl(PR_SET_DUMPABLE, 0)` on the agent process.
- **No `ptrace`.** `prctl(PR_SET_DUMPABLE, 0)` blocks `ptrace` too; additionally the agent's systemd unit sets `NoNewPrivileges=yes` and restricts `SYS_PTRACE` via seccomp.
- **No `/proc/pid/mem`.** Disabled by `PR_SET_DUMPABLE=0`.
- **No swap, ever.** Swap is disabled at the kernel level at image build time (see [F2](threat_model.md#41-the-mnemonic-and-derived-keys)).
- **Shutdown handler.** On SIGTERM the agent explicitly zeros every buffer, then exits. If the OS is going down, it does not wait for journald to flush.
## 7. Failure modes and corner cases
### 7.1 Invalid mnemonic entered
- UX rejects the phrase, clears the input buffer, prompts again.
- After N=3 failed attempts on the Pi LCD form factor, optionally offer to generate a new one (since the user clearly does not have their phrase).
### 7.2 User wants to switch identities mid-session
- `nostr-id lock` panic button zeros the current mnemonic.
- The session is now unusable for any signed action; user is prompted to re-enter a mnemonic or shut down.
- All apps connected to the signer socket receive a "signer unavailable" error; they are expected to fail closed.
### 7.3 Agent crashes
- The systemd unit restarts it, but the restart has no mnemonic loaded.
- All apps see "signer unavailable" until the user re-enters the mnemonic.
- The session is effectively over. We do not attempt to persist "oh just re-load from somewhere" — the mnemonic is user-only input.
### 7.4 Clock skew at first boot
- Nostr signatures don't depend on the wall clock, but relay event timestamps do.
- Identity-agent does not care about clock. Event-emitting apps do. Addressed separately by network-bootstrap via NTP.
### 7.5 User generates a new identity but never writes it down
- This is a user problem. The UX should make non-confirmation *very* obvious (the 3-random-word-position confirmation step in §3.2 Flow 2).
### 7.6 External NIP-46 bunker goes offline mid-session
- Signing fails. Apps see signer errors. If the bunker comes back, resume.
- If the bunker is permanently gone, the session is effectively read-only and the user must shut down.
## 8. Implementation plan (Phase 3a)
Each step is independently testable and each leans on an existing `nostr_core_lib` primitive rather than reimplementing crypto.
1. **Specify schedule.** Lock the index table in §2.1 behind a version number. Ship it inside a shared constants file (`stack/identity/schedule.toml` or equivalent) so all apps reference the same source of truth.
2. **Build `libnostr_core.a` under Alpine/musl.** Every identity-related binary is built following the same Alpine+musl discipline [`nostr_core_lib`](../includes/nostr_core_lib/), [`includes/ginxsom`](../includes/ginxsom/), and [`includes/c-relay`](../includes/c-relay/) already use — see [`plans/tui_login.md §0.1`](tui_login.md#01-toolchain-alpine--musl-dev-built-in-docker) for the full toolchain recipe. We reuse the shared `stack/build-common/Dockerfile.alpine-musl` landed in TUI step 1. No glibc, no `.deb`; the library is linked statically into each consumer binary produced from this builder.
3. **Prototype CLI `nostr-id`.** Single fully static C99 binary (`nostr-id_static_x86_64`) produced by the Alpine/musl builder, linking [`libnostr_core.a`](../includes/nostr_core_lib/). Reads mnemonic from stdin, calls [`nostr_derive_keys_from_mnemonic`](../includes/nostr_core_lib/nostr_core/nip006.h:24) for the requested role index, prints the npub via [`nostr_key_to_bech32`](../includes/nostr_core_lib/nostr_core/nostr_core.h). No daemon yet; no IPC; pure unit-testable against [`includes/nostr_core_lib/tests`](../includes/nostr_core_lib/tests/). `ldd` on the resulting binary must report "not a dynamic executable."
4. **Wrap as daemon.** Turn the CLI into a long-running agent:
- Holds mnemonic + all derived keys in `mlock()`-ed memory.
- Exposes a Unix-domain control socket at `/run/nostr-id/control.sock` for CLI commands.
- Runs the NIP-46 signer loop via [`nostr_nip46_signer_session_init`](../includes/nostr_core_lib/nostr_core/nip046.h:163) + [`nostr_nip46_signer_handle_request`](../includes/nostr_core_lib/nostr_core/nip046.h:169), receiving requests over a local Unix socket (or over a loopback relay for mesh-capable apps).
- Wires [`nostr_set_log_callback`](../includes/nostr_core_lib/nostr_core/nostr_log.h) to route library logs to journald (no on-disk log files, per [F10F11 in threat_model.md](threat_model.md#42-on-disk-state)).
- Handles SIGTERM by zeroing keys and exiting cleanly.
5. **Write TUI front end.** Curses-style boot-time entry for amd64 TTY. Accepts word-by-word mnemonic input, validates each word against the BIP-39 list, validates the checksum via [`nostr_derive_keys_from_mnemonic`](../includes/nostr_core_lib/nostr_core/nip006.h:24) (return code surfaces an invalid mnemonic). Generation flow uses [`nostr_generate_mnemonic_and_keys`](../includes/nostr_core_lib/nostr_core/nip006.h:21). External-signer flow uses [`nostr_nip46_client_session_init`](../includes/nostr_core_lib/nostr_core/nip046.h:145) with the user-supplied bunker URL.
6. **Retrofit Phase 2 services.**
- c-relay: switch [`iso/config/includes.chroot/etc/systemd/system/c-relay.service`](../iso/config/includes.chroot/etc/systemd/system/c-relay.service) to pull admin pubkey via `nostr-id show npub c-relay-admin` and admin privkey via `nostr-id derive c-relay-admin` (raw-key path, gated).
- ginxsom: same pattern, role `ginxsom` (NIP-06 index 3).
- fips: same pattern, role `fips` (NIP-06 index 4). Also receives its FIPS IPv6 from the agent.
- [`iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot) becomes much thinner — identity generation moves to the agent entirely.
7. **Smoke tests.** Extend [`iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) to verify:
- Agent is running and the control socket is present.
- Given a fixed test mnemonic (`test vector v1`), the 5 role npubs match hard-coded expected values — this guards against accidental changes to derivation semantics and is easy to cross-check against `nostr_core_lib`'s own tests.
- No `nsec1`, no 64-char lowercase hex that looks like a privkey, appears in `journalctl` output.
- NIP-46 ping round-trips over the signer socket via [`nostr_nip46_client_ping`](../includes/nostr_core_lib/nostr_core/nip046.h:156).
8. **LCD front end for Pi.** Comes later, as part of the Pi-Zero-2-W image flavor. Consumes the same control socket as the amd64 TUI.
9. **Runtime self-test.** On boot, identity-agent runs a quick self-test (derive a known-vector mnemonic → compare output to embedded expected npub) before declaring itself ready. If the library is miscompiled or `libsecp256k1` is missing, this fails fast with a visible error instead of silently producing wrong keys.
## 9. Open questions
- **Mnemonic strength.** 12 words = 128 bits of entropy. Is this enough? BIP-39 also supports 15/18/21/24-word phrases for 160/192/224/256 bits. n-OS-tr defaults to 12 for UX; 24 could be opt-in. Decision: default 12, allow longer phrases without breaking derivation.
- **Per-relay ephemeral keys.** Do we want a fresh throwaway key *per relay connection* for metadata hygiene? That's beyond NIP-06 indexing; probably an app-level concern (client rotates its own subkeys off index 1 via BIP-32 sub-derivation). Revisit in a later doc.
- **Hardware wallets.** `nostr-id bunker-connect` is a generic path for external signers, but hardware-wallet-specific UX (pair, unlock, confirm-on-device) is richer. Defer to Phase 4.
- **Identity handover.** Is there a safe way to pass a live session from one n-OS-tr machine to another without the user re-typing the mnemonic? E.g., QR-code export from one machine to another over a camera. Non-goal for v1; note for later.
- **Amnesia mode.** Some users may want to boot *without* a mnemonic at all (guest session, hostile environment). That means generating a key that exists only for this boot and is never written anywhere. Flow is: `nostr-id amnesia` — create ephemeral main key, skip config projection, live session only. Worth shipping in v1.

382
plans/iso_architecture.md Normal file
View File

@@ -0,0 +1,382 @@
# n-OS-tr ISO Architecture — Design Notes
This document captures the key design decisions for the n-OS-tr Debian Live
ISO build. It is the companion to the phased todo list and is intended to
survive individual work sessions.
## 1. Big-picture goal
A single bootable/installable Debian Live ISO that, the moment it finishes
booting, is running:
- [`fips`](../includes/fips) — Rust mesh-routing daemon, npub-addressed IPv6 overlay
- [`c-relay`](../includes/c-relay) — C Nostr relay with event-based config and embedded admin
- [`ginxsom`](../includes/ginxsom) — C Blossom (blob) server as FastCGI behind nginx
- `nginx` — TLS front door, static blob serving, reverse proxy
No Docker. No `curl | bash` provisioning. All three apps run as native
systemd services directly on the host.
## 2. Repo layout (target)
```
/ project root
├── live-build/ upstream live-build vendored
├── lb-wrapper.sh thin wrapper for ./live-build/frontend/lb
├── iso/ the live-build config (promoted from tutorial2/)
│ ├── auto/
│ │ ├── config lb config invocation with our flags
│ │ ├── build optional
│ │ └── clean optional
│ ├── config/
│ │ ├── package-lists/
│ │ │ ├── base.list.chroot
│ │ │ ├── fips-deps.list.chroot
│ │ │ ├── tor.list.chroot (optional)
│ │ │ ├── gui.list.chroot (optional)
│ │ │ └── installer.list.chroot (optional)
│ │ ├── hooks/
│ │ │ └── live/
│ │ │ ├── 0010-fetch-artifacts.hook.chroot
│ │ │ └── 0020-enable-services.hook.chroot
│ │ ├── includes.chroot/
│ │ │ ├── etc/systemd/system/*.service
│ │ │ ├── etc/nginx/sites-available/n-os-tr.conf
│ │ │ ├── etc/nginx/fastcgi_params
│ │ │ ├── etc/nginx/mime.types
│ │ │ ├── usr/local/sbin/n-os-tr-firstboot
│ │ │ ├── var/www/html/index.html
│ │ │ └── etc/issue
│ │ └── SHA256SUMS pinned artifact digests
│ └── local-artifacts/ optional offline override (gitignored)
├── includes/ the three component projects
│ ├── c-relay/
│ ├── ginxsom/
│ └── fips/
├── plans/ this doc and future design notes
└── README.md
```
Legacy scripts ([`root.sh`](../root.sh), [`nginx.sh`](../nginx.sh),
[`strfry.sh`](../strfry.sh), [`deploy.sh`](../deploy.sh),
[`test.sh`](../test.sh), [`docker.sh`](../docker.sh),
[`n-os-tr.sh`](../n-os-tr.sh)) are obsolete for the ISO build. They can
move to `scratch/` for historical reference or be deleted.
## 3. Runtime architecture on the booted ISO
```mermaid
flowchart TB
subgraph Ext[External]
U1([Internet peer])
U2([LAN host])
U3([Tor client])
end
subgraph Host[n-OS-tr booted ISO]
direction TB
Ng[nginx<br/>:80 :443]
C[c-relay<br/>127.0.0.1:8888]
G[ginxsom FastCGI<br/>/tmp/ginxsom-fcgi.sock]
F[fips daemon<br/>fips0 TUN]
Fd[fips-dns<br/>:5354]
Blobs[/var/www/blobs/]
RelayDb[/var/lib/c-relay/]
GinxDb[/var/lib/ginxsom/]
FbUnit[n-os-tr-firstboot<br/>oneshot]
FbUnit -.generates keys.-> KeyStore[/var/lib/n-os-tr/]
KeyStore -.EnvironmentFile.-> G
KeyStore -.identity.-> F
Ng -->|wss /relay| C
Ng -->|GET /sha256| Blobs
Ng -->|FastCGI PUT DELETE LIST HEAD| G
G --> Blobs
G --> GinxDb
C --> RelayDb
end
U1 --> Ng
U2 --> Ng
U3 -.onion.-> Ng
U1 -.npub IPv6.-> F
```
Notes:
- `c-relay` generates its own admin keypair on first start and prints the
nsec to the journal once. This is c-relay's native behavior and we should
not override it.
- `ginxsom` needs `--server-privkey` from **somewhere**. Today
[`ginxsom.service`](../includes/ginxsom/ginxsom.service:16) hardcodes one.
We replace that with an `EnvironmentFile` populated by the first-boot unit.
- `fips` can run ephemeral by default; persistent mode drops a `fips.key`
next to [`fips.yaml`](../includes/fips/packaging/common/fips.yaml:1).
## 4. Boot sequence
```mermaid
sequenceDiagram
participant LB as live-boot / initramfs
participant Sd as systemd PID 1
participant Fb as n-os-tr-firstboot.service
participant Fp as fips.service
participant Cr as c-relay.service
participant Gx as ginxsom.service
participant Ng as nginx.service
LB->>Sd: hand off
Sd->>Fb: start oneshot (Before= the app units)
alt First boot
Fb->>Fb: mkdir /var/lib/n-os-tr<br/>generate ginxsom key<br/>generate fips key<br/>write EnvironmentFile<br/>touch .provisioned
else Already provisioned
Fb-->>Sd: exit 0 (ConditionPathExists trip)
end
Fb-->>Sd: done
par
Sd->>Fp: start (reads /etc/fips/fips.yaml)
and
Sd->>Cr: start (self-provisions admin key to /var/lib/c-relay/)
and
Sd->>Gx: start (spawn-fcgi, reads env file for privkey)
and
Sd->>Ng: start (serves /var/www/blobs, proxies to c-relay and ginxsom)
end
```
## 5. Phase 0.A — Gitea artifact fetch
### Problem
Three binaries/packages are built in Gitea and must land inside the chroot
during `lb build`. We will not commit binaries to this repo.
### Strategy
A chroot hook [`0010-fetch-artifacts.hook.chroot`](../iso/config/hooks/live/0010-fetch-artifacts.hook.chroot)
that:
1. Checks for [`iso/local-artifacts/`](../iso/local-artifacts) first (offline
dev builds). If present, copy from there and skip the network path.
2. Otherwise `curl`s each artifact from Gitea release URLs. Auth via
`GITEA_TOKEN` environment variable passed through to the hook via
live-build's `--bootstrap-options` or a `.env`-style sourced file in
`auto/config`.
3. Verifies each download against [`iso/SHA256SUMS`](../iso/SHA256SUMS).
Any mismatch fails the build.
4. Installs:
- `fips_*.deb``dpkg -i` (pulls in systemd units, `fipsctl`, `fipstop`)
- `c_relay_x86``/opt/c-relay/c_relay_x86`, chmod 0755, owned by
system user `c-relay`
- `ginxsom-fcgi_static_x86_64``/usr/local/bin/ginxsom/ginxsom-fcgi`
5. Removes the download staging directory and runs `apt-get clean`.
### Open questions (in todos)
- Are the Gitea repos/releases public or do they need a token?
- How do we pass the token through to `lb build` (it runs as root)?
- Do we build `fips` from source via `cargo-deb` inside the chroot as
a fallback, or only consume a prebuilt `.deb`?
## 6. Phase 2.B — nginx + TLS template
### Listening surface
| Path | Method | Handler | Rationale |
|---|---|---|---|
| `/` | GET | static index.html from `/var/www/html/` | Landing page |
| `/relay` | GET upgrade | `proxy_pass http://127.0.0.1:8888` with WebSocket upgrade headers | c-relay Nostr wss |
| `/admin/` | GET | `proxy_pass http://127.0.0.1:8888/api/` | c-relay embedded admin |
| `^/[a-f0-9]{64}$` | GET HEAD | `try_files /var/www/blobs/$uri =404` | Direct disk serve — [`ginxsom's`](../includes/ginxsom/README.md:33) core design |
| `/upload` | PUT HEAD | `fastcgi_pass unix:/tmp/ginxsom-fcgi.sock` | Authenticated upload |
| `^/[a-f0-9]{64}$` | DELETE | `fastcgi_pass unix:/tmp/ginxsom-fcgi.sock` | Authenticated delete |
| `/list/` | GET | `fastcgi_pass unix:/tmp/ginxsom-fcgi.sock` | Per-pubkey listing |
| `/mirror` | PUT | `fastcgi_pass unix:/tmp/ginxsom-fcgi.sock` | BUD-04 |
| `/report` | PUT | `fastcgi_pass unix:/tmp/ginxsom-fcgi.sock` | BUD-09 |
### TLS posture — chosen default
**Self-signed cert generated on first boot**, valid for the box's hostname
and any DNS names in `/etc/n-os-tr/tls-names`. Reason: the ISO has no idea
what domain name it will live under, and most first users will run on a
LAN or behind another reverse proxy. A self-signed cert means wss:// works
out of the box for clients that trust it.
We ship an enable-certbot helper script for users who have a real domain:
```
n-os-tr-certbot <domain>
```
This stops nginx, runs `certbot certonly --standalone`, rewrites the cert
paths in the nginx config, and restarts nginx.
### Key nginx template snippets
WebSocket proxy to c-relay (the Upgrade dance is mandatory):
```nginx
location /relay {
proxy_pass http://127.0.0.1:8888;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400;
}
```
Direct blob serve with FastCGI fallback for non-GET:
```nginx
# GET of a 64-hex path: serve straight from disk
location ~ "^/(?<hash>[a-f0-9]{64})$" {
root /var/www/blobs;
try_files /$hash @ginxsom;
}
location @ginxsom {
fastcgi_pass unix:/tmp/ginxsom-fcgi.sock;
include fastcgi_params;
}
```
Uploads:
```nginx
location /upload {
client_max_body_size 100m;
fastcgi_pass unix:/tmp/ginxsom-fcgi.sock;
include fastcgi_params;
}
```
## 7. Phase 3.C — First-boot provisioning
### Unit
```ini
# /etc/systemd/system/n-os-tr-firstboot.service
[Unit]
Description=n-OS-tr first-boot key provisioning
ConditionPathExists=!/var/lib/n-os-tr/.provisioned
Before=c-relay.service ginxsom.service fips.service nginx.service
RequiredBy=c-relay.service ginxsom.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/n-os-tr-firstboot
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
```
### The script
Outline of `/usr/local/sbin/n-os-tr-firstboot`:
```bash
#!/bin/bash
set -euo pipefail
STATE=/var/lib/n-os-tr
KEYS=$STATE/keys
ENV=$STATE/env
mkdir -p "$STATE" "$KEYS" /var/www/blobs /var/lib/c-relay /var/lib/ginxsom
chmod 0700 "$KEYS"
# 1. ginxsom server keypair
if [[ ! -f $KEYS/ginxsom.nsec ]]; then
nak key generate > "$KEYS/ginxsom.nsec"
chmod 0400 "$KEYS/ginxsom.nsec"
fi
GINX_SEC=$(nak decode "$(cat "$KEYS/ginxsom.nsec")" | jq -r .private_key)
# 2. fips identity (optional persistent mode)
# If operator wants persistent identity, uncomment:
# if [[ ! -f /etc/fips/fips.key ]]; then
# fips keygen > /etc/fips/fips.key
# chmod 0600 /etc/fips/fips.key
# fi
# 3. self-signed TLS cert if none
if [[ ! -f /etc/ssl/n-os-tr/fullchain.pem ]]; then
mkdir -p /etc/ssl/n-os-tr
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
-subj "/CN=$(hostname)" \
-keyout /etc/ssl/n-os-tr/privkey.pem \
-out /etc/ssl/n-os-tr/fullchain.pem
fi
# 4. Write the environment file consumed by ginxsom.service
cat > "$ENV" <<EOF
GINXSOM_SERVER_PRIVKEY=$GINX_SEC
EOF
chmod 0400 "$ENV"
# 5. Ownership
chown -R www-data:www-data /var/www/blobs
chown -R c-relay:c-relay /var/lib/c-relay 2>/dev/null || true
touch "$STATE/.provisioned"
```
Notes:
- c-relay is intentionally **not** provisioned here. On first start it
generates its own admin keypair and prints the nsec to the journal. The
operator is expected to grab it from `journalctl -u c-relay`.
- The `ginxsom.service` we ship is a modified version that replaces the
hardcoded `--server-privkey` with `--server-privkey ${GINXSOM_SERVER_PRIVKEY}`
and adds `EnvironmentFile=-/var/lib/n-os-tr/env`.
### MOTD hint
`/etc/motd` will include:
```
Welcome to n-OS-tr.
Your Nostr relay admin key was generated on first boot. Retrieve it with:
journalctl -u c-relay | grep -i 'admin'
Your Blossom server pubkey:
cat /var/lib/n-os-tr/keys/ginxsom.nsec
Your FIPS identity:
fipsctl identity
```
## 8. Identity / persistence posture
Three supported modes, one default:
| Mode | Who it's for | What happens on reboot |
|---|---|---|
| **Ephemeral (default)** | Demos, privacy-maximal users, disposable relays | All keys regenerate, all data is lost |
| **Persistence partition** | Users running from USB who want continuity | Second partition labeled `persistence` with `persistence.conf` listing `/var/lib/n-os-tr`, `/var/lib/c-relay`, `/var/lib/ginxsom`, `/var/www/blobs` as `union` mounts. Keys and data survive reboots of that stick. |
| **Installed to disk** | Long-running nodes | Normal Debian behavior; user ran the debian-installer from the live session |
All three work from the same ISO image. The user chooses posture by how
they write the ISO and what partitions they create.
## 9. Known traps / risks
1. **c-relay has its own port** (8888) and its own `/api/` admin path,
while nginx also wants to serve `/admin/`. We either reverse-proxy
`/admin/``localhost:8888/api/` or expose c-relay's admin on a
separate port bound to localhost and forward it over SSH only.
2. **`ginxsom` expects spawn-fcgi** per the unit file. Make sure
`spawn-fcgi` is in `base.list.chroot`.
3. **libwebsockets ABI** — the c-relay binary is built static-musl per
[`STATIC_MUSL_GUIDE.md`](../includes/c-relay/STATIC_MUSL_GUIDE.md), so
we don't need to match the libwebsockets soname in Debian. Good.
4. **ginxsom static binary** is x86_64 only today; arm64 comes in Phase 6.
5. **`fips` Debian package** declares its own systemd units; don't
duplicate them via `includes.chroot` — install via `dpkg -i` and let
the postinst do the work.
6. **Live-build caches aggressively**; remember `lb clean --purge` between
structural changes.
## 10. Success criteria for the first real ISO
A single end-to-end demo, run from a booted QEMU VM:
- [ ] `systemctl status fips` → active, `fipsctl identity` prints an npub
- [ ] `systemctl status c-relay` → active, `journalctl -u c-relay` shows the admin nsec printed once
- [ ] `systemctl status ginxsom nginx` → both active
- [ ] `curl -k https://localhost/` returns the landing page
- [ ] `websocat wss://localhost/relay` accepts a NIP-01 `REQ` and returns an `EOSE`
- [ ] `curl -k -X PUT https://localhost/upload --data-binary @foo.txt` with a signed authorization header stores a blob, then `curl -k https://localhost/<sha256>` retrieves the same bytes

370
plans/network_boot.md Normal file
View File

@@ -0,0 +1,370 @@
# Network Booting the n-os-tr ISO from the Internet
## Status: **Paused / On Hold**
This document captures the design thinking, hardware validation, and implementation
plan for letting a client machine boot the [n-os-tr live ISO](../iso/) directly from
a URL on the internet — no local installation media required.
**Current state:** feasibility fully validated on the target hardware. Server-side
infrastructure not yet built. Resume here when ready.
---
## 1. The Goal
Make it possible to power on a bare-metal machine, press a key, and have it boot
straight into a running n-os-tr live environment whose kernel, initrd, and root
filesystem were downloaded over HTTPS from a server on the public internet at
boot time. No USB stick, no local disk, no LAN-side boot infrastructure required.
This complements — rather than replaces — the ISO file we already produce in
[`build.sh`](../build.sh). The ISO remains the primary deliverable; network boot
becomes a second distribution channel for that same set of artifacts.
---
## 2. Options Considered
Five distinct network-boot approaches were evaluated. They are listed in order
of "how close to the goal of booting from an internet URL with nothing on the LAN".
### Option 1: UEFI HTTP(S) Boot (firmware-native)
Modern UEFI 2.5+ firmware (≈2015 and newer) can itself fetch a `.efi` bootloader
over HTTP/HTTPS from any URL. No PXE server, no TFTP, no USB stick. You enter
the URL into the BIOS (or supply it via DHCP option 67) and the firmware does
the rest.
- **Pros:** zero LAN-side infrastructure, cleanest flow, one-time BIOS setup.
- **Cons:** requires UEFI 2.5+ with the feature exposed; older BIOSes don't have it;
many consumer BIOSes hide or restrict the option.
- **Selected as primary path for this project.** See §4 for hardware validation.
### Option 2: Classic PXE with iPXE chainload
BIOS uses its PXE Option ROM to DHCP on the LAN and TFTP-load a bootloader.
That bootloader is [iPXE](https://ipxe.org), which then has full HTTPS support
and fetches everything else from the internet.
- **Pros:** works on essentially any BIOS from ~2005 onward. Well-documented.
- **Cons:** requires a DHCP and TFTP server on the LAN. More infrastructure setup.
- **Secondary fallback** if HTTP Boot ever becomes unavailable.
### Option 3: iPXE on a USB stick (or CD)
Flash the ~1 MB [`ipxe.usb`](https://boot.ipxe.org/ipxe.usb) image to any USB drive
and boot from USB. iPXE launches, reaches the internet over HTTPS, and fetches
the ISO assets. Once the stick is made, it's effectively a permanent
"network boot" key — the ISO lives on the internet and can be updated server-side
without touching the stick.
- **Pros:** works on any machine that can boot USB (i.e. all of them). Zero
BIOS configuration needed. Zero LAN infrastructure needed.
- **Cons:** requires distributing a physical medium one time per machine.
- **Universal fallback** when neither HTTP Boot nor PXE are usable.
### Option 4: netboot.xyz
Hosted iPXE menu service at [netboot.xyz](https://netboot.xyz). Flash their USB
or point PXE at their URL; get a menu of hundreds of bootable OSes. Can be
extended with custom menu entries for your own ISO.
- **Pros:** no build step needed; community-maintained.
- **Cons:** dependency on third-party service; extra indirection for a
single-ISO use case.
- **Not chosen.** More value for general-purpose multi-distro booting than for
a single project's distribution channel.
### Option 5: GRUB with `http` + `loopback` modules
GRUB 2 can load `.iso` files over HTTP and loopback-mount them using the
`http` and `net` modules. Requires GRUB already installed somewhere
(local disk, USB, or chainloaded).
- **Pros:** familiar bootloader; loopback-mount of real ISOs.
- **Cons:** niche; less common tooling; redundant next to iPXE.
- **Not chosen.**
---
## 3. Decision Tree
```mermaid
flowchart TD
Start[Want to netboot ISO from the internet] --> Q1{UEFI HTTP Boot<br/>available?}
Q1 -->|Yes| UEFI[Configure URL in BIOS<br/>firmware fetches .efi]
Q1 -->|No| Q2{BIOS supports<br/>PXE?}
Q2 -->|Yes| Q3{Can you run a<br/>LAN DHCP+TFTP server?}
Q3 -->|Yes| PXE[Local DHCP+TFTP<br/>chainload iPXE to HTTPS]
Q3 -->|No| USB1[iPXE USB stick]
Q2 -->|No| USB2[iPXE USB stick]
UEFI --> ISO[ISO served from HTTPS]
PXE --> ISO
USB1 --> ISO
USB2 --> ISO
```
---
## 4. Hardware Validation (Target: ASRock B650I Lightning WiFi)
The target platform is the ASRock B650I Lightning WiFi motherboard (AMD AM5,
Ryzen 7000 series CPUs, 2.5 GbE Realtek RTL8125BG, Wi-Fi 6E).
Reference: [`B650I Lightning WiFi.pdf`](../B650I%20Lightning%20WiFi.pdf).
### 4.1 Firmware Facts
From the manual's Section 1.2 Specifications:
- **BIOS family:** AMI Aptio V UEFI ("AMI UEFI Legal BIOS with GUI support", p. 5)
- **Onboard LAN:** Dragon RTL8125BG 2.5 GbE (p. 3)
- **Wi-Fi:** 802.11ax Wi-Fi 6E module (p. 3)
- **BIOS Flashback button:** present (Section 2.13, pp. 4748) — allows BIOS
update with no CPU, RAM, or GPU installed.
### 4.2 BIOS Revision Tested
- **Current BIOS on the unit:** version `3.01` (released 2024-05-14,
AGESA 1.1.7.0)
- **Latest stable at time of writing:** `4.10` (2026-03-03, AGESA 1.3.0.0a)
- None of the release notes between `1.28` (initial) and `4.10` mention
Network Stack / HTTP Boot / PXE changes — the feature set for UEFI networking
has been stable across all revisions. Updating is therefore not expected to
add or remove HTTP Boot capability.
### 4.3 CSM (Legacy BIOS Compatibility)
- CSM cannot be persistently enabled on this board. Every attempt resets to
disabled on reboot. This is **normal and expected on AM5** — AMD removed
legacy Option ROM support at the silicon level in Ryzen 7000, so CSM is a
vestigial menu item only. The platform is UEFI-only by design.
- This is the correct state for UEFI HTTP Boot; no action required.
### 4.4 BIOS Menu Layout Discovered
The Network Stack menu layout on this specific BIOS revision is **partial**:
-`Advanced → IPv4 Network Configuration` (exposed)
-`Advanced → IPv6 Network Configuration` (exposed)
- ❌ Parent "Network Stack" toggle with `Ipv4 PXE Support` / `Ipv4 HTTP Support`
sub-options — **not visible in the UI on BIOS 3.01**.
- ✅ However, the `Advanced → Onboard Devices Configuration` submenu does exist
on this board (confirmed by the warning text on ASRock's BIOS download page
referencing `BIOS\Advanced\Onboard Devices Configuration\Display Priority`).
Typical options there: Change LEDs, Display Priority, HD Audio, Onboard LAN,
WAN, Bluetooth toggles — but **no Network Stack option inside** on 3.01.
### 4.5 HTTP Boot Status: **ENABLED AND ACTIVE**
Empirical probe via the F11 one-shot boot menu revealed two UEFI network
boot entries already present:
```
UEFI: PXE IPv4 Realtek PCIe GbE Family Controller
UEFI: HTTP IPv4 Realtek PCIe GbE Family Controller ← this one, confirmed present
```
On this BIOS revision ASRock ships Network Stack **auto-enabled**
with both PXE and HTTP IPv4 supported, even though the granular toggle for
HTTP Support is not surfaced in the Setup UI. No BIOS configuration change
is required to use HTTP Boot on this machine.
### 4.6 Wi-Fi Boot
- **Not supported.** The Wi-Fi 6E module is not exposed to the UEFI network
stack. Network boot requires wired Ethernet plugged in at POST time.
Once the live system is running, Wi-Fi works normally (via Linux drivers,
not firmware).
### 4.7 Summary Table
| Capability | Status on this board with BIOS 3.01 |
|---|---|
| UEFI HTTP Boot (IPv4) | ✅ Active and ready |
| UEFI HTTPS Boot | ⚠️ Likely requires certificate enrollment; not investigated |
| UEFI PXE (IPv4) | ✅ Active |
| Legacy CSM/PXE | ❌ Not possible on AM5 platform |
| Wi-Fi boot | ❌ Not supported by firmware |
| BIOS Flashback for recovery/upgrade | ✅ Available (no CPU/RAM/GPU required) |
**Bottom line:** the hardware is ideal for this use case. Zero firmware work
is needed; the only remaining task is building the server-side delivery chain.
---
## 5. Target Architecture
```mermaid
flowchart TD
subgraph Client [Client machine B650I or compatible]
A[Power on → F11 boot menu]
A2[Select UEFI HTTP IPv4]
end
subgraph Server [Public HTTPS server host TBD]
B[bootx64.efi<br/>~1MB iPXE UEFI binary<br/>with embedded script]
C[boot.ipxe<br/>boot script]
D[vmlinuz<br/>Linux kernel]
E[initrd.img<br/>Debian live initramfs]
F[filesystem.squashfs<br/>compressed root FS]
end
A --> A2
A2 -->|1. HTTP GET bootx64.efi| B
B -->|2. firmware launches iPXE| IPXE[iPXE running]
IPXE -->|3. HTTPS GET boot.ipxe| C
C -->|4. HTTPS GET kernel and initrd| D
C --> E
E -->|5. initramfs HTTPS GET fetch=squashfs| F
F --> Live[n-os-tr live system running]
```
### Protocol Notes
- **Step 1 (firmware → bootx64.efi): plain HTTP.** UEFI 2.5+ firmware generally
supports HTTPS boot only with CA certificates enrolled into the firmware
trust store — extra complexity, and on ASRock consumer boards this path is
not well-documented. Plain HTTP is acceptable for this one hop because:
1. The binary is ≤ 1 MB and trivial to re-serve.
2. iPXE binaries can be code-signed separately and/or hash-verified.
3. All subsequent traffic is HTTPS.
- **Steps 35 (iPXE and live-boot): HTTPS.** iPXE has modern TLS support and
`live-boot` uses standard libcurl/wget semantics via the `fetch=URL` kernel
parameter. No special configuration required to use HTTPS throughout.
### Artifact Inventory
| File | Size | Source | Hosted at (example) |
|---|---|---|---|
| `bootx64.efi` | ~1 MB | Built from [`iPXE`](https://github.com/ipxe/ipxe) with embedded script | `http://boot.example/bootx64.efi` |
| `boot.ipxe` | ~1 KB | Hand-written | `https://boot.example/n-os-tr/boot.ipxe` |
| `vmlinuz` | ~815 MB | `live-build` output at `binary/live/vmlinuz` | `https://boot.example/n-os-tr/vmlinuz` |
| `initrd.img` | ~3050 MB | `live-build` output at `binary/live/initrd.img` | `https://boot.example/n-os-tr/initrd.img` |
| `filesystem.squashfs` | ~12 GB | `live-build` output at `binary/live/filesystem.squashfs` | `https://boot.example/n-os-tr/filesystem.squashfs` |
---
## 6. Implementation Plan (When Resumed)
### Phase 1: Proof-of-Concept — Build iPXE
- [ ] Clone [`github.com/ipxe/ipxe`](https://github.com/ipxe/ipxe).
- [ ] Create an embedded script `myscript.ipxe` that DHCPs and chainloads
`https://boot.example/n-os-tr/boot.ipxe`.
- [ ] Enable in `src/config/general.h`:
- `DOWNLOAD_PROTO_HTTPS`
- `NET_PROTO_IPV6` (optional but recommended)
- [ ] Build: `make bin-x86_64-efi/ipxe.efi EMBED=myscript.ipxe`.
- [ ] Upload resulting `.efi` to the webserver as `bootx64.efi`.
### Phase 2: Choose and Configure the Hosting
- [ ] Decide where to host:
- Plain nginx/Apache on a VPS.
- The project's own [`includes/ginxsom/`](../includes/ginxsom/) Blossom
server — natural fit for the static squashfs given content-addressing.
- An object store (S3 / R2 / Backblaze) fronted by a CDN.
- [ ] Ensure the host serves `.efi` with an appropriate MIME type
(`application/octet-stream` or `application/efi`).
- [ ] Verify HTTP (not HTTPS) works for the `bootx64.efi` endpoint — some
hosts redirect HTTP→HTTPS by default and the UEFI firmware may not
follow redirects.
### Phase 3: Extend `build.sh` to Publish Artifacts
- [ ] After `lb build` completes, copy `binary/live/vmlinuz`, `initrd.img`,
and `filesystem.squashfs` out of the build tree.
- [ ] Push them to a versioned path on the webserver
(e.g. `/n-os-tr/v${VERSION}/...`).
- [ ] Update a `/n-os-tr/latest/` symlink or alias.
- [ ] Regenerate `boot.ipxe` to reference the correct versioned paths.
- [ ] Consider integrity: publish a SHA-256 manifest alongside the artifacts
so iPXE's `imgverify` / manual verification can catch tampering even
over plain HTTP.
### Phase 4: Write `boot.ipxe`
```ipxe
#!ipxe
set base-url https://boot.example/n-os-tr/latest
echo Loading n-os-tr from ${base-url}
kernel ${base-url}/vmlinuz boot=live components quiet splash \
fetch=${base-url}/filesystem.squashfs
initrd ${base-url}/initrd.img
boot
```
- [ ] The `fetch=URL` parameter is handled by Debian's
[`live-boot`](https://manpages.debian.org/testing/live-boot-doc/live-boot.7.en.html)
initramfs; it downloads the squashfs into RAM and pivots root to it.
- [ ] The `iso/` build already includes `live-boot` via
[`iso/config/package-lists/base.list.chroot`](../iso/config/package-lists/base.list.chroot) —
confirm at resume time.
### Phase 5: Optional — DHCP Option 67 for Hands-Free Boot
If the LAN's DHCP server is controllable, set DHCP option 67
(Boot-File-Name) so the client firmware auto-receives the URL instead of
prompting at boot.
- **ISC dhcpd:**
```conf
option arch code 93 = unsigned integer 16;
class "httpclients" {
match if substring (option vendor-class-identifier, 0, 10) = "HTTPClient";
option vendor-class-identifier "HTTPClient";
if option arch = 00:10 {
filename "http://boot.example/bootx64.efi";
}
}
```
- **dnsmasq:**
```conf
dhcp-vendorclass=set:httpboot,HTTPClient
dhcp-boot=tag:httpboot,http://boot.example/bootx64.efi
```
- **Result:** F11 → HTTP IPv4 → boots with zero user input.
### Phase 6: End-to-End Test
- [ ] Fresh boot of the B650I target with wired Ethernet connected.
- [ ] F11 → `UEFI: HTTP IPv4 Realtek …` → enter (or auto-receive) URL.
- [ ] Confirm iPXE banner appears.
- [ ] Confirm `boot.ipxe` is fetched and executed.
- [ ] Confirm kernel + initrd load.
- [ ] Confirm squashfs streams in and live system boots to login prompt.
- [ ] Confirm [`n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot)
and [`n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest)
behave identically to the USB-boot case.
---
## 7. Open Questions for When We Resume
1. **Where are we hosting the artifacts?** Public VPS with nginx, project-owned
ginxsom/Blossom, object store + CDN, or something else?
2. **Versioning strategy:** `/latest/` symlink, or require client to supply a
version in the URL? Rollback semantics?
3. **HTTPS Boot (instead of plain HTTP) for the first hop:** worth the
certificate-enrollment effort, or is integrity via out-of-band hash
pinning enough?
4. **Signed iPXE:** do we want to build a Secure Boot-signed iPXE so that
Secure Boot can remain enabled on clients? Shim + MOK, or use an existing
signed build?
5. **Multi-arch:** is arm64 (aarch64) netboot ever in scope, or x86_64 only?
(If yes, second iPXE build for `bin-arm64-efi/ipxe.efi`.)
6. **Observability:** do we want a log/telemetry endpoint so that network-
booted clients phone home on successful boot?
7. **Fallback distribution:** still ship the full `.iso` for USB installs?
(Answer assumed "yes" — network boot is additive, not a replacement.)
---
## 8. Known Limitations
- **Wired Ethernet required at boot time.** No Wi-Fi netboot on consumer UEFI.
- **Plain HTTP for the bootloader hop** unless certificate enrollment is done;
mitigate with hash verification.
- **Boot speed bounded by internet throughput** for the ~12 GB squashfs. A
cached/CDN-fronted origin is strongly recommended for realistic fleet use.
- **No offline fallback** by design — this is complementary to the regular USB
ISO, not a replacement.
- **BIOS 3.01 on the target doesn't expose a UI toggle** for `Ipv4 HTTP
Support` but the feature is active by default. If a future BIOS revision
changes this, revisit via F11 inspection as in §4.5.
---
## 9. References
- iPXE project: <https://ipxe.org>
- iPXE build targets: <https://ipxe.org/appnote/buildtargets>
- iPXE scripting reference: <https://ipxe.org/scripting>
- Debian `live-boot(7)`:
<https://manpages.debian.org/testing/live-boot-doc/live-boot.7.en.html>
- Debian Live Manual — runtime behaviours:
<https://live-team.pages.debian.net/live-manual/html/live-manual/customizing-run-time-behaviours.en.html>
- UEFI HTTP Boot overview (UEFI spec §24): <https://uefi.org/specifications>
- ASRock B650I Lightning WiFi BIOS downloads:
<https://www.asrock.com/mb/AMD/B650I%20Lightning%20WiFi/index.asp#BIOS>
- Project ISO architecture sibling plan: [`iso_architecture.md`](./iso_architecture.md)
- Project build entry point: [`build.sh`](../build.sh)
- Project live-build wrapper: [`lb-wrapper.sh`](../lb-wrapper.sh)
- Running / usage docs: [`docs/RUNNING.md`](../docs/RUNNING.md)

View File

@@ -0,0 +1,367 @@
# Nostr Config Projection — "Nostr as your home directory"
> **Status:** Design. This document defines how n-OS-tr projects a user's configuration from Nostr events into the live filesystem at boot, and how it publishes changes back to Nostr at shutdown.
>
> Depends on: [`plans/identity_subsystem.md`](identity_subsystem.md) (must run first to load the main key), [`plans/threat_model.md`](threat_model.md) (defines what this subsystem is and is not allowed to do).
## 1. Concept
Your n-OS-tr configuration — dotfiles, preferences, bookmarks, per-app state — is **not** stored on the machine. It's stored as **replaceable Nostr events, signed by your main key (NIP-06 index 0), encrypted to yourself with [NIP-44](https://github.com/nostr-protocol/nips/blob/master/44.md)**.
At boot, after identity is loaded, the **config-loader** queries a set of user relays for these events, decrypts them, and writes the content into the correct locations under `/home/$USER/...` on the ephemeral rootfs. At shutdown (or on explicit `nostr-config save`), the **config-writer** re-emits changed files as new signed replaceable events.
The consequence:
- **Same 12 words on any n-OS-tr machine = same environment.** Your `.bashrc`, your SSH `authorized_keys`, your Firefox bookmarks, your relay list, your app preferences all reappear.
- **No machine knows anything about you after shutdown.** The tmpfs is wiped; the Nostr events live on relays (encrypted) and on paper in your head (the mnemonic).
- **Only you can read your config.** NIP-44 encrypts to your own npub; relay operators see ciphertext only.
## 1.1 Crypto foundation: `nostr_core_lib`
Like [`identity_subsystem.md`](identity_subsystem.md#11-crypto-foundation-nostr_core_lib), the config-loader and config-writer are built on top of [`includes/nostr_core_lib`](../includes/nostr_core_lib/). Concretely:
| Capability | `nostr_core_lib` symbol | NIP |
|---------------------------------------------|------------------------------------------------------------------------------------------------|------------|
| Create + sign replaceable slot events | [`nostr_create_and_sign_event`](../includes/nostr_core_lib/nostr_core/nostr_core.h) | NIP-01 |
| NIP-44 self-encrypt slot plaintext | [`nostr_nip44_encrypt(priv, pub_self, plaintext, out, ...)`](../includes/nostr_core_lib/nostr_core/nip044.h:28) | NIP-44 |
| NIP-44 self-decrypt fetched ciphertext | [`nostr_nip44_decrypt(priv, pub_self, ciphertext, out, ...)`](../includes/nostr_core_lib/nostr_core/nip044.h:62) | NIP-44 |
| Query relays for slot events (sync) | [`synchronous_query_relays_with_progress`](../includes/nostr_core_lib/nostr_core/nostr_core.h) | NIP-01 |
| Publish slot events | [`synchronous_publish_event_with_progress`](../includes/nostr_core_lib/nostr_core/nostr_core.h)| NIP-01 |
| Relay pool with reconnection (streaming) | [`nostr_relay_pool_create`](../includes/nostr_core_lib/nostr_core/nostr_core.h) + [`nostr_relay_pool_subscribe`](../includes/nostr_core_lib/nostr_core/nostr_core.h) | NIP-01 |
| NIP-42 auth (personal private relay) | [`nostr_relay_pool_set_auth`](../includes/nostr_core_lib/nostr_core/nostr_core.h) | NIP-42 |
| NIP-11 relay info (feature check) | [`nostr_nip11_fetch_relay_info`](../includes/nostr_core_lib/nostr_core/nostr_core.h) | NIP-11 |
| Relay-level event signing proxied via agent | NIP-46 path through [`nostr_nip46_client_sign_event`](../includes/nostr_core_lib/nostr_core/nip046.h:158) — config-writer never holds the user's raw private key | NIP-46 |
Two important consequences of the `nostr_core_lib` choice:
1. **Config-writer does not need the user's private key.** It talks to identity-agent over NIP-46 (local Unix socket), and each slot event is signed by the agent on the writer's behalf. The raw main nsec stays inside the agent's `mlock()`-ed memory per [F3 / F4 of the threat model](threat_model.md#41-the-mnemonic-and-derived-keys).
2. **The encryption target is the user's own pubkey.** NIP-44 "self-encryption" uses `priv_main` + `pub_main` as both ends of the ECDH; the library supports this directly (both arguments in [`nostr_nip44_encrypt`](../includes/nostr_core_lib/nostr_core/nip044.h:28)). Decryption uses the same `(priv_main, pub_main)` pair.
## 2. The model
### 2.1 Slots
A **slot** is a single unit of user configuration. Each slot is:
- One replaceable Nostr event.
- Keyed by a stable `d` tag identifying the slot (the "address" in NIP-33 terms).
- Content is either plaintext (for public/non-secret things like a relay list that you *want* to be reuseable by other clients) or NIP-44 ciphertext encrypted to the user's own npub (the default for anything remotely personal).
- Signed by the user's main key (NIP-06 index 0).
### 2.2 Manifest event
The **manifest** is a single special slot that lists every other slot the user wants the OS to materialize. The manifest is itself a replaceable event with a well-known `d` tag:
```
d = "n-os-tr:manifest:v1"
```
Its content is a JSON document whose schema is:
```json
{
"version": 1,
"generated_at": 1714000000,
"slots": [
{
"name": "dotfiles/bashrc",
"path": "/home/user/.bashrc",
"mode": "0644",
"encrypted": true
},
{
"name": "ssh/authorized_keys",
"path": "/home/user/.ssh/authorized_keys",
"mode": "0600",
"encrypted": true
},
{
"name": "nostr/relays",
"path": "/home/user/.config/n-os-tr/relays.json",
"mode": "0644",
"encrypted": false
}
]
}
```
The manifest event is also NIP-44-encrypted to self by default. A user who explicitly wants their manifest public (so other clients can share data through n-OS-tr's slot scheme) can opt into unencrypted manifest publishing.
### 2.3 Event kinds
| Purpose | Kind | NIP | Notes |
|-----------------------|-------|------------|---------------------------------------------------------|
| Slot event (config) | 30078 | [NIP-78](https://github.com/nostr-protocol/nips/blob/master/78.md) ("application-specific data") | Parameterized replaceable, user-chosen `d` tag. Matches existing convention for app state. |
| Manifest | 30078 | NIP-78 | `d = "n-os-tr:manifest:v1"`. |
| Role table | 30078 | NIP-78 | `d = "n-os-tr:roles:v1"`. Maps role name → NIP-06 index. See [`plans/identity_subsystem.md`](identity_subsystem.md#23-role-registration-protocol). |
| Relay list | 10002 | [NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md) | Optional; if present, config-loader prefers NIP-65 for discovering user relays. |
Choosing kind **30078** (NIP-78 application-specific data) aligns with existing clients and avoids claiming a new kind. All slot events use the same kind; they're distinguished by their `d` tag.
### 2.4 Tag conventions
Every slot event carries these tags:
```
["d", "<slot name>"] required, unique per slot
["client", "n-os-tr"] identifies which app wrote it
["ver", "<semver>"] optional, version of the slot schema
["sha256", "<hex>"] optional, hash of plaintext content
["path", "<target path>"] optional, overrides manifest path if present
["encrypted","nip44"] present if content is NIP-44 ciphertext
```
## 3. Boot flow
```mermaid
sequenceDiagram
autonumber
participant IA as identity-agent
participant CL as config-loader
participant Sig as signer socket
participant R as user relays
participant FS as tmpfs /home
Note over IA: mnemonic loaded<br/>(see identity_subsystem.md)
CL->>Sig: connect, get npub for "main"
Sig-->>CL: npub1...
CL->>R: REQ kind=10002 author=npub (NIP-65 relay list)
R-->>CL: user relays (cacheable)
CL->>R: REQ kind=30078 author=npub d="n-os-tr:manifest:v1"
R-->>CL: manifest ciphertext event
CL->>Sig: NIP-44 decrypt(manifest.content)
Sig-->>CL: manifest JSON
loop for each slot in manifest
CL->>R: REQ kind=30078 author=npub d=slot.name
R-->>CL: slot event (ciphertext or plaintext)
alt ciphertext
CL->>Sig: NIP-44 decrypt
Sig-->>CL: plaintext
end
CL->>FS: write plaintext to slot.path with slot.mode
end
Note over CL,FS: home dir materialized
CL->>IA: signal "ready"
IA->>+Session: start user session
```
### 3.1 Relay discovery
Config-loader needs to know where to query. Options, in priority order:
1. **Built-in bootstrap relays.** A tiny hardcoded list baked into the image (overridable per user). Used to fetch NIP-65 and manifest on a first-ever session from this mnemonic.
2. **NIP-65 relay list (kind 10002).** Once fetched, becomes the authoritative set for this user.
3. **User-provided relay list via kernel cmdline.** For edge cases where bootstrap relays are not reachable (offline/air-gapped setups), the user may pass `nostr.relay=wss://my-relay.example` on the boot cmdline.
4. **Local c-relay running on this machine.** If the user is running their own personal c-relay and it already has their events cached, config-loader will query it first and fall back to the network only for misses.
### 3.2 Timing and ordering
- Config-loader is a systemd unit with `Requires=nostr-id.service After=nostr-id.service network-online.target`.
- User session targets (display manager, shell, GUI) `After=config-loader.service`.
- config-loader blocks session start until it has either materialized the manifest or timed out.
- Timeout defaults to 15 seconds. On timeout, user is prompted: "No relays reachable. Continue without config? (y/N)". The fallback is an empty home (every boot pretends to be first boot).
### 3.3 First-ever boot with this mnemonic
If no manifest event exists yet (the npub has never published one), config-loader treats that as the "new user" case:
- Creates an empty manifest.
- Proceeds to materialize nothing.
- User session starts with a blank home; any changes can be captured by the first save.
## 4. Save flow
```mermaid
sequenceDiagram
autonumber
participant User
participant CW as config-writer
participant Sig as signer socket
participant FS as /home
participant R as user relays
User->>CW: trigger (explicit save, shutdown, periodic)
CW->>FS: scan tracked paths for changes
FS-->>CW: list of modified slots
loop for each modified slot
CW->>FS: read plaintext
alt slot.encrypted
CW->>Sig: NIP-44 encrypt(plaintext, to=self)
Sig-->>CW: ciphertext
CW->>Sig: sign event(kind=30078, d=slot, content=ciphertext, tags=...)
else slot.encrypted == false
CW->>Sig: sign event(kind=30078, d=slot, content=plaintext, tags=...)
end
Sig-->>CW: signed event
CW->>R: publish
end
CW->>Sig: sign updated manifest (if slot list changed)
CW->>R: publish manifest
```
### 4.1 When does save happen?
Three triggers, in increasing scope:
1. **Explicit.** `nostr-config save [slot-name]` — user-initiated save of one slot or all.
2. **Shutdown.** On SIGTERM from systemd (clean shutdown), config-writer does a full save pass before poweroff continues. Hard-capped by a timeout so a bad relay doesn't block shutdown.
3. **Periodic (opt-in).** For users who want continuous sync, an opt-in `periodic=true` flag in the manifest causes config-writer to run on a 5-minute timer. Off by default, because continuous publishing is visible metadata on the relay.
### 4.2 What gets saved
Config-writer is **not** a generic home-directory sync. It only touches files listed in the manifest. Anything outside the manifest is ephemeral by definition — which is exactly what we want.
Users who want to track a new file:
```bash
nostr-config add-slot --name "dotfiles/vimrc" --path "/home/user/.vimrc" --mode 0644 --encrypted
```
This updates the manifest and does an immediate save.
### 4.3 Detecting changes
Each slot event carries a `sha256` tag with the hash of the plaintext at publish time. Config-writer compares the current file hash to the last-saved hash; if different, the slot is saved. This avoids republishing unchanged files.
## 5. Conflict handling
Two n-OS-tr sessions, different machines, same mnemonic, both editing the same slot. Whose write wins?
Nostr's replaceable-event semantics say: most recent `created_at` wins. So:
- Each slot event is tagged with a monotonic `created_at`.
- On save, config-writer stamps the event with `now`.
- Relays dedupe on `(author, kind, d)` keeping the latest `created_at`.
### 5.1 Detecting conflicts
When materializing at boot, config-loader also fetches the *previous* event ID (if any) and records it in `/run/nostr-config/state.json`. At save time, if a slot's remote event has advanced beyond what the loader saw at boot, we have a conflict:
| Scenario | Config-writer behavior |
|-------------------------------------------|-----------------------------------------------------------------------------------|
| Local modified, remote unchanged | Normal save. Publish new event. |
| Local unchanged, remote advanced | No action. The old data is already overwritten on relay; we don't downgrade. |
| Local modified **and** remote advanced | Conflict. User is prompted: show local diff vs remote diff, choose one or merge. |
| Local modified, remote has multiple updates | Conflict with multi-option chooser. |
For phase-1 we implement:
- Detect
- Back up local plaintext to `/run/nostr-config/conflicts/<slot>.local`
- Prefer remote by default in non-interactive modes (shutdown timeout)
- Prompt on `nostr-config save` interactive runs.
### 5.2 Manifest version tag
The manifest itself carries a `version` integer. Session start reads the version; at save, if the remote manifest version is higher than what we started with, refuse to overwrite without an explicit merge step.
## 6. Example slot table
Concrete projection from slots to paths that the default n-OS-tr user will likely want:
| Slot name | Target path | Encrypted | Notes |
|-----------------------------------|-------------------------------------------|-----------|------------------------------------|
| `dotfiles/bashrc` | `/home/user/.bashrc` | yes | |
| `dotfiles/bash_aliases` | `/home/user/.bash_aliases` | yes | |
| `dotfiles/vimrc` | `/home/user/.vimrc` | yes | |
| `dotfiles/gitconfig` | `/home/user/.gitconfig` | yes | Contains name/email |
| `ssh/authorized_keys` | `/home/user/.ssh/authorized_keys` | yes | |
| `ssh/config` | `/home/user/.ssh/config` | yes | |
| `nostr/relays` | `/home/user/.config/n-os-tr/relays.json` | no | Can be NIP-65 if user prefers |
| `nostr/blossom-servers` | `/home/user/.config/n-os-tr/blossom.json` | no | User's preferred Blossom endpoints |
| `nostr/follows` | derived from kind 3 (NIP-02) directly | n/a | Not stored via this mechanism |
| `firefox/bookmarks.json` | `/home/user/.mozilla/firefox/profile/bookmarks.json` | yes | Optional, app-specific |
| `n-os-tr/roles` | `/run/nostr-id/roles.json` | yes | Managed by identity-agent, not user-edited |
| `motd` | `/etc/motd` | no | "hello, $YOUR_NPUB" |
Not every slot needs to be present. The manifest enumerates only what the user chose to save.
## 7. Privacy properties
- **Confidentiality.** All personal slots are NIP-44 ciphertext. The ciphertext is IND-CCA2 under NIP-44's construction (xchacha20-poly1305 with derived keys). Relay operators observe ciphertext.
- **Metadata exposure.** Relay operators still see: your npub, the kind (30078), the `d` tag (which is the human-readable slot name, e.g., "ssh/config"!), the `created_at`, and the event size.
- Mitigation 1: run your own personal c-relay (NIP-06 index 2). Sync only there by default. Use remote relays as backup only.
- Mitigation 2: optionally hash the `d` tag (`d = blake3(user_secret || slot_name)`). Debatable whether the loss of debuggability is worth the metadata win. Not v1.
- **Replay / rollback attacks.** A malicious relay could serve an old `created_at` version of a slot. Mitigation: on boot, ask multiple relays and take the newest. If relays disagree, log a warning. For high-stakes slots (like `ssh/authorized_keys`), require agreement across N>=2 relays.
- **Timing correlation.** Every n-OS-tr boot publishes a burst of events. An adversary observing relay traffic could correlate "these events came from the same boot." Mitigation: randomized delays between slot publishes; stagger manifest vs slot writes. Weak mitigation; users who need full anonymity should torify relay connections.
## 8. What this subsystem is NOT
- **Not a general filesystem-sync tool.** Only named slots are tracked. `git` repos, dev databases, photo libraries, anything large — all stay ephemeral.
- **Not a binary blob store.** Slots are meant to be small JSON/text config. For photos, app binaries, datasets, etc., use Blossom via [`ginxsom`](../includes/ginxsom/).
- **Not real-time sync.** Boot and shutdown by default. Periodic save is opt-in. Continuous multi-device sync is not a goal in v1.
- **Not a backup system.** If all your relays forget your events simultaneously, your config is gone. Run your own c-relay. Pin events you care about.
- **Not a place for secrets you wouldn't want a future-broken NIP-44 to reveal.** NIP-44 uses well-studied modern crypto, but any long-term encrypted-at-rest design ages. Do not use config slots to store, e.g., your Bitcoin seed. Slots are for *OS configuration*; secrets of last resort belong elsewhere (or nowhere).
## 9. Interaction with Phase 2 services
Today's services generate their own fresh keys via [`n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot). Phase 3 reworks this:
- Service keys come from identity-agent (see [`plans/identity_subsystem.md`](identity_subsystem.md#57-integration-with-current-services)).
- Service configuration comes from config-loader via slots:
- `nostr/relays` → used by `c-relay` to decide which outbound peers to talk to.
- `nostr/blossom-servers` → used by `ginxsom` for upstream mirroring.
- `fips/peers` → used by `fips` to bootstrap.
- None of this fundamentally changes the services themselves; it just replaces the "where does config live" question.
## 10. Implementation plan (Phase 3b)
Follow Phase 3a (identity-agent). All steps build on the `nostr_core_lib` primitives in [§1.1](#11-crypto-foundation-nostr_core_lib):
1. **Spec the event schema.** Nail down kind, tags, manifest JSON schema. Write it as `stack/config-projection/schema.md` plus a JSON Schema file that config-loader and config-writer both validate against on read/write.
2. **Prototype `nostr-config-loader` as a one-shot CLI.** Single fully static C99 binary linked against [`libnostr_core.a`](../includes/nostr_core_lib/) and built under the shared Alpine/musl toolchain (see [`plans/tui_login.md §0.1`](tui_login.md#01-toolchain-alpine--musl-dev-built-in-docker)). Output: `nostr-config-loader_static_<arch>`, `ldd` reports "not a dynamic executable."
- Discovers user relays (bootstrap list → NIP-65 kind 10002 via [`synchronous_query_relays_with_progress`](../includes/nostr_core_lib/nostr_core/nostr_core.h)).
- Fetches manifest (kind 30078, `d="n-os-tr:manifest:v1"`).
- Decrypts via [`nostr_nip44_decrypt(priv_main, pub_main, ...)`](../includes/nostr_core_lib/nostr_core/nip044.h:62) — main privkey fetched from identity-agent, kept in locked memory only for the duration of the call.
- For each slot in the manifest: single batch REQ with multiple `#d` values; decrypt each with `nostr_nip44_decrypt`; write to `slot.path` with `slot.mode`.
- Exits when done; leaves nothing on disk except the materialized slot files.
3. **Prototype `nostr-config-writer` as a one-shot CLI.**
- Reads manifest from `/run/nostr-config/manifest.json` (cached by the loader at boot).
- Detects changed files via sha256 diff against cached hashes.
- For each changed slot: encrypts with [`nostr_nip44_encrypt`](../includes/nostr_core_lib/nostr_core/nip044.h:28), signs via identity-agent NIP-46 ([`nostr_nip46_client_sign_event`](../includes/nostr_core_lib/nostr_core/nip046.h:158)), publishes via [`synchronous_publish_event_with_progress`](../includes/nostr_core_lib/nostr_core/nostr_core.h).
- Republishes the manifest if the slot set changed.
- Respects a hard timeout so shutdown doesn't block on a bad relay.
4. **Wire into systemd.**
- `nostr-config-loader.service``Type=oneshot`, `After=nostr-id.service network-online.target`, `Before=multi-user.target`. Materializes home before user login.
- `nostr-config-writer.service``Type=oneshot`, runs on `systemctl stop multi-user.target` path (drop-in in `shutdown.target.wants/`). Saves before poweroff.
- `nostr-config-save.timer` (opt-in) — fires every 5 minutes when enabled via manifest `periodic=true`.
5. **CLI: `nostr-config`.** Wraps the two binaries with convenience commands:
- `nostr-config status` — report on manifest version, last save time, unsaved changes.
- `nostr-config save [slot|--all]` — force save.
- `nostr-config load [slot|--all]` — pull remote and overwrite local (with confirm).
- `nostr-config add-slot --name --path --mode [--encrypted|--plain]` — edit manifest.
- `nostr-config remove-slot <name>` — edit manifest.
- `nostr-config diff <slot>` — show local vs remote.
- `nostr-config list` — print the slot table.
6. **Smoke test.** Extend [`iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest):
- Given a known test mnemonic and a pre-populated local c-relay seeded with canned slot events, after `nostr-config-loader` runs, verify specific files exist at specific paths with specific sha256 sums.
- After modifying a file and running `nostr-config save`, verify the local c-relay has a new kind-30078 event whose NIP-44-decrypted content matches the file.
- Verify the manifest event's `created_at` advances when the slot set changes.
- Verify that `journalctl -u nostr-config-loader` and `journalctl -u nostr-config-writer` contain no `nsec1` or 64-char lowercase hex that looks like a privkey.
7. **Conflict UI.** Simple interactive chooser for `nostr-config save` when a conflict is detected (per §5.1). Non-interactive mode preserves remote and writes the local version to `/run/nostr-config/conflicts/<slot>.local`.
8. **Periodic save (opt-in).** Timer unit, rate-limited. Explicit opt-in in the manifest.
9. **Self-test on boot.** Before the user's first session, loader does a manifest round-trip against a local c-relay (if present) or drops into "offline mode" cleanly. If NIP-44 decrypt fails on a slot (e.g., wrong mnemonic), user is warned *before* anything is materialized.
## 11. Open questions
- **How many slots is "too many" for a 15-second boot?** Rough math: each slot ~1-2 KB, one REQ round-trip per slot on first boot. With 50 slots and a fast relay, ~2 seconds total. We should still cap slot count (e.g., 200) and advise users with huge configs to self-host a relay.
- **Batch fetches.** Can we issue a single REQ for all slots in the manifest and demux by `d`? Yes, NIP-01 filters allow multiple `#d` values. That's the intended optimization.
- **Storing arbitrarily-large files as slots.** Not supported (see §8). But it's tempting. The line to hold is: slots are config, Blossom is blobs.
- **Cross-device concurrent edits.** Phase-1 strategy is "last writer wins with conflict detection." More sophisticated CRDT-style merging is out of scope.
- **Encrypted manifest but unencrypted individual slots.** Is that a useful mode? Probably not — if the manifest is encrypted, we can list everyone's slots publicly just by guessing `d` tags. Recommendation: manifest and slots encrypt or don't together.
- **`d`-tag namespace collisions.** We use prefixes like `dotfiles/`, `ssh/`, `firefox/`. Should we formalize an IANA-ish registry inside the repo? Probably yes: `stack/config-projection/slot-registry.md` lists known slot names and their paths, so different n-OS-tr versions don't create divergent conventions for the same thing.
- **Schema migration.** What happens when we bump the manifest schema from v1 to v2? The `ver` tag identifies schema version; a future loader refuses to materialize a manifest with an unsupported `version` and prompts for an explicit migration.

View File

@@ -0,0 +1,644 @@
# Slice B + C: Add fips and c-relay to the n-OS-tr ISO
## Status: **Ready for implementation**
This document specifies the work required to enable the [fips](../includes/fips)
mesh-routing daemon and the [c-relay](../includes/c-relay) Nostr relay on the
live ISO built by [`build.sh`](../build.sh), replacing the current Slice A
(ginxsom + nginx only) with the full three-service stack planned in
[`iso_architecture.md`](./iso_architecture.md).
---
## 1. Design Decisions (captured from user)
| # | Topic | Decision |
|---|---|---|
| 1 | Source of c-relay binary | User-provided, already downloaded into [`includes/c-relay/`](../includes/c-relay) |
| 2 | Source of fips binaries | Build locally via `cargo deb` during `build.sh` |
| 3 | c-relay admin nsec strategy | **Ephemeral** — c-relay self-provisions per boot, admin nsec captured from journald |
| 4 | fips identity | **Ephemeral** — new npub per boot, no persistent key management for this slice |
| 5 | fips peer config | Connect to the same public bootstrap node used elsewhere in the repo: `npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98` at `217.77.8.91:2121` (UDP), per [`examples/sidecar-nostr-relay/.env`](../includes/fips/examples/sidecar-nostr-relay/.env:9) et al. |
| 6 | fips transport | **Ethernet** enabled, plus UDP for the static peer |
| 7 | fips-gateway | Disabled (default) |
| 8 | c-relay exposure | nginx reverse-proxies `wss://host/relay``ws://127.0.0.1:8888/`; nginx reverse-proxies `/admin/``http://127.0.0.1:8888/api/` |
| 9 | Scope | Full Slice B+C — services, nginx wiring, package deps, smoke test, MOTD, README updates |
| 10 | Key management / identity continuity | Out of scope for this slice — revisit in a later pass |
---
## 2. What Already Exists (Slice A baseline)
Confirmed present and working, per source inspection:
- [`iso/auto/config`](../iso/auto/config) — live-build config
- [`iso/config/package-lists/base.list.chroot`](../iso/config/package-lists/base.list.chroot) — nginx-light, spawn-fcgi, ca-certificates, curl, jq, vim, htop
- [`iso/config/includes.chroot/etc/systemd/system/ginxsom.service`](../iso/config/includes.chroot/etc/systemd/system/ginxsom.service)
- [`iso/config/includes.chroot/etc/systemd/system/n-os-tr-firstboot.service`](../iso/config/includes.chroot/etc/systemd/system/n-os-tr-firstboot.service)
- [`iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf`](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf) — `:443` TLS, Blossom routing, static landing page
- [`iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot) — generates ginxsom nsec + self-signed TLS
- [`iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) — 5 PASS + 8 SKIP placeholder
- [`iso/config/includes.chroot/usr/local/bin/nak`](../iso/config/includes.chroot/usr/local/bin/nak) — Nostr CLI helper
- [`iso/config/includes.chroot/usr/local/bin/ginxsom/ginxsom-fcgi`](../iso/config/includes.chroot/usr/local/bin/ginxsom/ginxsom-fcgi) — Blossom FastCGI binary
- [`iso/config/hooks/live/0020-enable-services.hook.chroot`](../iso/config/hooks/live/0020-enable-services.hook.chroot) — enables nginx, ginxsom, firstboot
Slice A smoke test passes `5 PASS, 0 FAIL, 8 SKIP` on a freshly-booted USB.
---
## 3. What Needs to Change (target state)
```mermaid
flowchart LR
subgraph Build [Build-time build.sh]
BS[build.sh]
CDB[cargo deb in<br/>includes/fips]
FDeb[iso/local-artifacts/fips_0.3.0_amd64.deb]
CRX[includes/c-relay/c_relay_x86]
CRC[iso/local-artifacts/c_relay_x86]
BS --> CDB --> FDeb
BS --> CRC
end
subgraph Chroot [live-build chroot phase]
H1[0010-fetch-artifacts.hook.chroot]
FDeb --> H1
CRC --> H1
H1 -->|dpkg -i| FInst[/usr/bin/fips etc]
H1 -->|install -m 0755| CInst[/opt/c-relay/c_relay_x86]
H1 -->|adduser --system| CUsr[c-relay system user]
H1 -->|write fips.yaml| FCfg[/etc/fips/fips.yaml]
end
subgraph Boot [Runtime at boot]
Fb[n-os-tr-firstboot]
Fips[fips.service]
FDns[fips-dns.service]
CRel[c-relay.service]
Gx[ginxsom.service]
Ng[nginx.service]
Fb --> Gx
Fips --> FDns
Ng -->|wss /relay| CRel
Ng -->|http /admin/| CRel
end
```
### Summary of changes
| Area | Change |
|---|---|
| Build system | [`build.sh`](../build.sh) gains a pre-build step: build fips `.deb` via `cargo deb` and stage artifacts into [`iso/local-artifacts/`](../iso/local-artifacts) |
| Artifact layout | New directory [`iso/local-artifacts/`](../iso/local-artifacts) holds `fips_*_amd64.deb` and `c_relay_x86` — gitignored |
| Chroot hook (new) | [`iso/config/hooks/live/0010-fetch-artifacts.hook.chroot`](../iso/config/hooks/live/0010-fetch-artifacts.hook.chroot) installs those two artifacts into the chroot |
| Package list | [`iso/config/package-lists/base.list.chroot`](../iso/config/package-lists/base.list.chroot) adds runtime deps: `libdbus-1-3`, `iproute2`, `nftables` (optional for gateway mode later) |
| fips config | New [`iso/config/includes.chroot/etc/fips/fips.yaml`](../iso/config/includes.chroot/etc/fips/fips.yaml) overriding the package default with ethernet + static UDP peer enabled |
| c-relay service | New [`iso/config/includes.chroot/etc/systemd/system/c-relay.service`](../iso/config/includes.chroot/etc/systemd/system/c-relay.service) — adapted from [upstream unit](../includes/c-relay/systemd/c-relay.service) |
| c-relay user | Chroot hook creates `c-relay` system user + `/opt/c-relay/` working dir |
| nginx config | [`iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf`](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf) gains `/relay` wss upgrade and `/admin/` proxy locations |
| Service-enable hook | [`0020-enable-services.hook.chroot`](../iso/config/hooks/live/0020-enable-services.hook.chroot) also enables `fips.service`, `fips-dns.service`, `c-relay.service` |
| Smoke test | [`n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) replaces the 8 SKIPs with real checks |
| MOTD / README | First-boot script and docs updated to surface how to find c-relay admin nsec and fips identity |
| `.gitignore` | Add `iso/local-artifacts/` |
---
## 4. Concrete Artifact Details
### 4.1 c-relay static binary
- **Source:** [`includes/c_relay_static_x86_64`](../includes/c_relay_static_x86_64) — user-downloaded static-musl build, already present at the top level of `includes/` (NOT inside `includes/c-relay/`). Filename confirmed by directory listing.
- **Staged as:** `iso/config/includes.chroot_before_packages/live-artifacts/c_relay_x86` (canonical name used by the chroot hook and systemd unit — we rename on copy so `c-relay.service` can reference a stable filename regardless of which upstream build variant the user drops in).
- **Target in chroot:** `/opt/c-relay/c_relay_x86`, mode `0755`, owner `c-relay:c-relay`.
- **Copied by:** `build.sh` copies [`includes/c_relay_static_x86_64`](../includes/c_relay_static_x86_64) → staging area → chroot hook installs to `/opt/c-relay/`.
### 4.2 `iso/local-artifacts/fips_0.3.0_amd64.deb`
- **Source:** built by `build.sh` via `cd includes/fips && packaging/debian/build-deb.sh`. Requires `cargo-deb` installed on the build host (`cargo install cargo-deb` if absent; `build.sh` will check and fail clearly).
- **Contents (per [`Cargo.toml`](../includes/fips/Cargo.toml:70-83)):**
- `/usr/bin/fips`, `/usr/bin/fipsctl`, `/usr/bin/fipstop`, `/usr/bin/fips-gateway`
- `/etc/fips/fips.yaml` (package default — will be overridden by our includes.chroot version)
- `/etc/fips/hosts`
- `/lib/systemd/system/fips.service`, `/lib/systemd/system/fips-dns.service`, `/lib/systemd/system/fips-gateway.service`
- `/usr/lib/fips/fips-dns-setup`, `/usr/lib/fips/fips-dns-teardown`
- `/usr/lib/tmpfiles.d/fips.conf`
- **Runtime deps (per Cargo.toml):** `libc6, systemd, libdbus-1-3`, recommends `bluez` (skipped — not needed for non-BLE).
- **Installed by:** chroot hook runs `dpkg -i /tmp/artifacts/fips_*_amd64.deb` then cleans up. The postinst enables `fips.service` and `fips-dns.service` automatically.
### 4.3 `iso/config/includes.chroot/etc/fips/fips.yaml`
Overrides the Debian-packaged default at `/etc/fips/fips.yaml`. The live-build
`includes.chroot` mechanism copies this on top after package install. Content:
```yaml
# n-OS-tr fips configuration.
# Ephemeral identity: new keypair every boot (default).
# To make identity persistent across reboots on a live USB with a
# persistence partition, set node.identity.persistent: true.
node:
identity:
# ephemeral by default
tun:
enabled: true
name: fips0
mtu: 1280
dns:
enabled: true
port: 5354
transports:
udp:
bind_addr: "0.0.0.0:2121"
tcp:
bind_addr: "0.0.0.0:8443"
ethernet:
# Ethernet transport auto-discovers LAN peers.
# interface: omitted → fips picks the first non-loopback interface.
# Override with an explicit interface name if your board uses
# something other than the default (e.g. enp5s0, eno1, eth0).
discovery: true
announce: true
auto_connect: true
accept_connections: true
# Static peer for internet bootstrap.
# Matches the same "fips-test-node" / "vps-chi" public node used across
# the repo's example configs (see includes/fips/examples/... and
# includes/fips/testing/sidecar/.env).
peers:
- npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98"
alias: "fips-test-node"
addresses:
- transport: udp
addr: "217.77.8.91:2121"
connect_policy: auto_connect
```
Notes:
- Omitting `ethernet.interface` relies on fips's auto-selection logic. If that
doesn't pick a sensible interface, add `interface: "eth0"` (verified post-boot
via `ip link show`).
- Keeping the `udp` bootstrap peer in addition to `ethernet` gives the mesh
internet reachability even when the LAN has no other fips peers.
### 4.4 `iso/config/includes.chroot/etc/systemd/system/c-relay.service`
Adapted from [upstream](../includes/c-relay/systemd/c-relay.service) with
minor tweaks for the live environment:
```ini
[Unit]
Description=C Nostr Relay (n-OS-tr slice C)
Documentation=file:///usr/share/doc/n-os-tr/README
After=network.target n-os-tr-firstboot.service
Wants=network-online.target
[Service]
Type=simple
User=c-relay
Group=c-relay
WorkingDirectory=/opt/c-relay
Environment=DEBUG_LEVEL=0
# c-relay self-provisions admin + relay keys on first start and prints
# the admin nsec to stdout (captured by journald).
ExecStart=/opt/c-relay/c_relay_x86 --debug-level=${DEBUG_LEVEL}
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=c-relay
# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/c-relay
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6
LimitNOFILE=65536
LimitNPROC=4096
[Install]
WantedBy=multi-user.target
```
### 4.5 Updated nginx site — `/relay` wss + `/admin/` proxy
New locations appended to [`iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf`](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf) inside the existing `server { listen 443 ssl; ... }` block, before the ginxsom FastCGI blocks:
```nginx
# --- c-relay reverse proxy (slice C) ---------------------------------
# Nostr relay WebSocket: wss://host/relay → ws://127.0.0.1:8888/
# The client library expects a WebSocket upgrade on /relay; c-relay
# itself listens on "/" so we rewrite and proxy to its root.
location = /relay {
proxy_pass http://127.0.0.1:8888/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
# NIP-11 relay info doc (available at wss://host/relay with
# Accept: application/nostr+json). Proxied to the same backend.
# If you want /relay (GET without upgrade) to return NIP-11 too,
# the location above already handles it — c-relay's NIP-11 handler
# triggers based on the Accept header, not the path.
# c-relay embedded web admin UI: https://host/admin/ → http://127.0.0.1:8888/api/
location /admin/ {
proxy_pass http://127.0.0.1:8888/api/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
Notes:
- The `/admin/` path deliberately overlaps with a Blossom admin path, but
Blossom's admin routes go via the FastCGI `@ginxsom` named location and
c-relay's admin is explicitly reverse-proxied — nginx's longest-prefix
match means `/admin/` → c-relay wins for GET/POST, and ginxsom admin is
reached via a different path (if ginxsom has one). **Potential conflict
with Slice A** — need to confirm ginxsom doesn't rely on `/admin/`.
Current [n-os-tr.conf:53](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf:53) has `location /admin/ { try_files $uri @ginxsom; }` — this will need to be **removed or renamed** (e.g. `/blossom-admin/`) to avoid collision. See §6 Open Questions.
- The landing page at `/` (static `/var/www/html/index.html`) is unaffected.
### 4.6 New chroot hook — `0010-fetch-artifacts.hook.chroot`
```sh
#!/bin/sh
# Install c-relay and fips into the chroot from iso/local-artifacts/.
set -e
ART=/live-artifacts
if [ ! -d "$ART" ]; then
echo "[fetch-artifacts] no $ART; skipping" >&2
exit 0
fi
# --- c-relay -----------------------------------------------------------
if [ -f "$ART/c_relay_x86" ]; then
echo "[fetch-artifacts] installing c-relay binary"
# Create system user and directory
if ! getent passwd c-relay >/dev/null 2>&1; then
adduser --system --no-create-home --group --home /opt/c-relay c-relay
fi
mkdir -p /opt/c-relay
install -m 0755 -o c-relay -g c-relay \
"$ART/c_relay_x86" /opt/c-relay/c_relay_x86
chown -R c-relay:c-relay /opt/c-relay
else
echo "[fetch-artifacts] WARNING: $ART/c_relay_x86 not found" >&2
fi
# --- fips --------------------------------------------------------------
FIPS_DEB=$(ls "$ART"/fips_*_amd64.deb 2>/dev/null | head -1 || true)
if [ -n "$FIPS_DEB" ] && [ -f "$FIPS_DEB" ]; then
echo "[fetch-artifacts] installing fips from $FIPS_DEB"
dpkg -i "$FIPS_DEB" || apt-get -f install -y
else
echo "[fetch-artifacts] WARNING: no fips_*_amd64.deb in $ART" >&2
fi
```
How `$ART` gets populated in the chroot: live-build copies
[`iso/local-artifacts/`](../iso/local-artifacts) into
`/live-artifacts/` inside the chroot via an `includes.chroot_before_packages/`
entry that bind-mounts or symlinks the directory, OR more simply, we place
the artifacts directly under
`iso/config/includes.chroot_before_packages/live-artifacts/` so live-build
copies them before package install. See §5 step 2 for the exact location.
### 4.7 Updated `build.sh`
Inserted **before** the `sudo "$REPO_ROOT/lb-wrapper.sh" config` line:
```bash
# --- Stage Slice B artifacts ------------------------------------------------
ARTIFACTS="$WORKSPACE_ISO_DIR/config/includes.chroot_before_packages/live-artifacts"
mkdir -p "$ARTIFACTS"
# 1. c-relay: copy the user-provided binary from includes/c-relay/
CRELAY_SRC=""
for candidate in \
"$REPO_ROOT/includes/c-relay/c_relay_x86" \
"$REPO_ROOT/includes/c-relay/build/c_relay_x86" \
"$REPO_ROOT/includes/c-relay/c-relay" ; do
if [[ -x "$candidate" ]]; then CRELAY_SRC="$candidate"; break; fi
done
if [[ -z "$CRELAY_SRC" ]]; then
echo "[build.sh] ERROR: no c-relay binary found. Looked for:" >&2
echo " includes/c-relay/c_relay_x86" >&2
echo " includes/c-relay/build/c_relay_x86" >&2
echo " includes/c-relay/c-relay" >&2
exit 1
fi
cp "$CRELAY_SRC" "$ARTIFACTS/c_relay_x86"
chmod 0755 "$ARTIFACTS/c_relay_x86"
echo "[build.sh] staged c-relay from $CRELAY_SRC"
# 2. fips: build the .deb (requires cargo-deb)
if ! command -v cargo-deb >/dev/null 2>&1; then
echo "[build.sh] ERROR: cargo-deb not installed. Run: cargo install cargo-deb" >&2
exit 1
fi
pushd "$REPO_ROOT/includes/fips" >/dev/null
packaging/debian/build-deb.sh
popd >/dev/null
FIPS_DEB=$(ls "$REPO_ROOT/includes/fips/deploy"/fips_*_amd64.deb | sort | tail -1)
if [[ -z "$FIPS_DEB" || ! -f "$FIPS_DEB" ]]; then
echo "[build.sh] ERROR: fips .deb build produced no artifact" >&2
exit 1
fi
cp "$FIPS_DEB" "$ARTIFACTS/"
echo "[build.sh] staged fips .deb: $(basename "$FIPS_DEB")"
```
### 4.8 Updated package list — `base.list.chroot`
Add runtime deps needed by fips (already pulled in transitively by the .deb
but explicit is safer) and by the smoke test:
```
nginx-light
spawn-fcgi
libfcgi-bin
ca-certificates
openssl
curl
jq
vim
htop
# Slice B/C additions:
libdbus-1-3
iproute2
nftables
```
### 4.9 Updated `0020-enable-services.hook.chroot`
```sh
#!/bin/sh
set -e
# Nginx
rm -f /etc/nginx/sites-enabled/default
ln -sf /etc/nginx/sites-available/n-os-tr.conf /etc/nginx/sites-enabled/n-os-tr.conf
systemctl enable nginx.service
# Slice A
systemctl enable ginxsom.service
systemctl enable n-os-tr-firstboot.service
# Slice B (fips) — units installed by the .deb postinst already enable
# them, but we're explicit for idempotence:
systemctl enable fips.service || true
systemctl enable fips-dns.service || true
# Slice C (c-relay)
systemctl enable c-relay.service
```
### 4.10 Updated smoke test
Replace the fips and c-relay SKIP blocks in [`n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) with real checks:
```bash
# -------------------- fips mesh daemon -----------------------------
section "fips mesh daemon"
check "fips.service active" systemctl is-active fips.service
check "fips-dns.service active" systemctl is-active fips-dns.service
check "fips0 interface present" ip link show fips0
# fipsctl identity should return an npub-shaped string
npub=$(fipsctl identity 2>/dev/null | head -1 | tr -d '[:space:]')
case "$npub" in
npub1*) pass "fipsctl identity returns npub" "${npub:0:20}..." ;;
*) fail "fipsctl identity returns npub" "got: ${npub:-<empty>}" ;;
esac
# fips UDP transport should be listening
check "fips UDP bound on :2121" bash -c 'ss -ulnp 2>/dev/null | grep -q ":2121"'
# -------------------- c-relay nostr relay --------------------------
section "c-relay nostr relay"
check "c-relay.service active" systemctl is-active c-relay.service
check "c-relay listening on :8888" bash -c 'ss -tlnp 2>/dev/null | grep -q ":8888"'
# The admin nsec should have been logged during first startup
if journalctl -u c-relay.service --no-pager 2>/dev/null | \
grep -q "Admin Private Key:"; then
pass "admin nsec logged in journal" "found"
else
fail "admin nsec logged in journal" "grep miss"
fi
# wss end-to-end via nginx, using the shipped nak:
if timeout 10 nak req --relay wss://localhost/relay -k 1 -l 1 \
>/dev/null 2>&1; then
pass "wss://localhost/relay answers REQ" "ok"
else
fail "wss://localhost/relay answers REQ" "timeout or error"
fi
# NIP-11 relay info doc
nip11=$(curl -sk -H 'Accept: application/nostr+json' \
https://localhost/relay 2>/dev/null)
if echo "$nip11" | jq -e '.name' >/dev/null 2>&1; then
pass "NIP-11 info doc reachable" "name=$(echo "$nip11" | jq -r .name)"
else
fail "NIP-11 info doc reachable" "no JSON .name"
fi
# c-relay embedded admin UI via nginx
status=$(curl -sk -o /dev/null -w '%{http_code}' https://localhost/admin/ 2>/dev/null)
case "$status" in
200|302|401) pass "admin UI reachable via nginx" "HTTP $status" ;;
*) fail "admin UI reachable via nginx" "HTTP $status" ;;
esac
```
Target final summary: `~15 PASS / 0 FAIL / 0 SKIP`.
### 4.11 MOTD and README updates
The [`n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot) script's MOTD block gains additional hints:
```
n-OS-tr — Nostr-addressed mesh node (preview)
Ginxsom (Blossom) server pubkey (hex):
$GINXSOM_SERVER_PUBKEY
retrieve nsec: cat /var/lib/n-os-tr/keys/ginxsom.nsec
c-relay Nostr relay:
wss://$(hostname)/relay (admin nsec: journalctl -u c-relay | grep 'Admin Private Key')
https://$(hostname)/admin/ (web admin UI)
fips mesh daemon:
fipsctl identity (show npub)
fipsctl peers (list peers)
Quick health check: n-os-tr-smoketest
```
The [`iso/config/includes.chroot/usr/share/doc/n-os-tr/README`](../iso/config/includes.chroot/usr/share/doc/n-os-tr/README) gets corresponding additions.
---
## 5. Build & Deploy Sequence
1. **Place c-relay binary** at `includes/c-relay/c_relay_x86` (or one of the
fallback paths `build.sh` checks). Ensure it's `chmod +x` and runs standalone.
2. **Install `cargo-deb`** on the build host: `cargo install cargo-deb`.
3. **Edit** all files listed in §3 according to §4.
4. **Move artifacts staging location:**
- New directory: `iso/config/includes.chroot_before_packages/live-artifacts/`
— gitignored.
- `build.sh` writes c-relay binary and fips .deb here.
- `0010-fetch-artifacts.hook.chroot` reads from `/live-artifacts/` inside
the chroot (live-build copies the `includes.chroot_before_packages` tree
into the chroot root before packages install).
5. **Add** `iso/config/includes.chroot_before_packages/live-artifacts/` to
[`.gitignore`](../.gitignore).
6. **Full clean build:** `sudo ./build.sh --clean` (needed because package
list changed).
7. **Burn** the resulting `iso/live-image-amd64.hybrid.iso` to USB with `dd`.
8. **Boot** on the B650I machine.
9. **Verify:** run `n-os-tr-smoketest` — expect all checks to PASS.
---
## 6. Open Questions / Known Conflicts
### 6.1 nginx `/admin/` path collision
Slice A's [n-os-tr.conf:53](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf:53)
routes `/admin/` to ginxsom. Slice C wants `/admin/` for c-relay.
**Resolution proposed:** change Slice A's Blossom admin to `/blossom/` (if
ginxsom supports path rewriting) or `/blossom-admin/`. Need to check
ginxsom's source to see whether its admin endpoints are hardcoded to `/admin/`
or whether the FastCGI layer can remap them.
### 6.2 Fips ethernet auto-interface selection
If the B650I's Ethernet shows up as `enp5s0` or similar and fips's
"auto-pick first non-loopback" heuristic grabs the wrong interface (e.g. Wi-Fi
— though we explicitly said runtime Wi-Fi is fine, we don't want fips
scanning it), we may need to hardcode `ethernet.interface: "enp5s0"` in
[fips.yaml](../iso/config/includes.chroot/etc/fips/fips.yaml). **Resolution:**
check `ip link show` on first boot; update if needed.
### 6.3 Stray file `=4` in [`includes/c-relay/`](../includes/c-relay)
Shown in the directory listing but not the c-relay binary (the real binary
is [`includes/c_relay_static_x86_64`](../includes/c_relay_static_x86_64)
at the top level of `includes/`). Likely a shell-redirect typo artifact
(`command >=4 file` or similar). Safe to delete during cleanup; does not
affect the build.
### 6.4 c-relay writable database location
c-relay writes `.nrdb` files to its `WorkingDirectory` (`/opt/c-relay/`). In
a live session with ephemeral rootfs this works — the writes go to the
tmpfs union layer. For persistence-partition boots we'd want `/opt/c-relay/`
on the persistence mount. **Not addressed in this slice** per decision #10.
### 6.5 fips running as root
The fips Debian package runs fips as root (needs TUN + raw sockets). The
hardening in [`fips.service`](../includes/fips/packaging/debian/fips.service:17-22)
is meaningful but fips itself is privileged. Acceptable for this slice.
### 6.6 Reverse proxy's X-Forwarded-Proto and c-relay's NIP-42
NIP-42 requires the relay to know its own URL (for the `["relay", "url"]` tag
in the AUTH challenge). c-relay has [logic](../includes/c-relay/src/nip042.c:103)
that falls back to `ws://127.0.0.1:PORT` — this means NIP-42 challenges will
reference the internal address, not `wss://host/relay`. Clients may then
fail auth. **Mitigation:** c-relay has a configurable `relay_url` config
key (set via admin event). Post-boot procedure documented in updated README
for users who want NIP-42 to work correctly. **Not blocking for basic
operation** — the relay accepts unauthenticated connections by default.
---
## 7. Success Criteria
From a booted USB on fresh hardware with wired Ethernet:
- [ ] `systemctl status fips-dns ginxsom c-relay nginx n-os-tr-firstboot` — all active
- [ ] `fipsctl identity` — prints a valid npub
- [ ] `fipsctl peers` — at minimum lists `fips-test-node` (may show as "connecting" if UDP blocked)
- [ ] `ip link show fips0` — fips0 TUN interface up
- [ ] `journalctl -u c-relay | grep 'Admin Private Key'` — returns the nsec (once)
- [ ] `curl -k https://localhost/` — landing page
- [ ] `curl -k -I https://localhost/admin/` — returns 200 or 302 (c-relay UI)
- [ ] `nak req --relay wss://localhost/relay -k 1 -l 1` — returns `["EOSE",...]` after an empty result
- [ ] `curl -ksI https://localhost/0000000000000000000000000000000000000000000000000000000000000000` — 404 (ginxsom still works)
- [ ] `n-os-tr-smoketest``15 PASS / 0 FAIL / 0 SKIP`
---
## 8. Rollback
If something goes sideways during development:
- The entire Slice A → Slice B+C diff is confined to these files/directories:
```
build.sh (additive step)
iso/config/hooks/live/0010-fetch-artifacts.hook.chroot (new)
iso/config/hooks/live/0020-enable-services.hook.chroot (edited)
iso/config/package-lists/base.list.chroot (edited)
iso/config/includes.chroot/etc/fips/ (new dir)
iso/config/includes.chroot/etc/systemd/system/c-relay.service (new)
iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf (edited)
iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot (edited motd)
iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest (edited)
iso/config/includes.chroot/usr/share/doc/n-os-tr/README (edited)
iso/config/includes.chroot_before_packages/ (new, gitignored)
.gitignore (edited)
```
- A revert is a `git checkout` of those paths plus removal of
`iso/config/includes.chroot_before_packages/`.
---
## 9. Out of Scope (deferred to later slices)
- Persistent identity (ginxsom nsec, c-relay admin nsec, fips npub survive reboots).
- Gitea release fetch for artifacts (current plan is local-only).
- c-relay `relay_url` auto-configuration for NIP-42 (manual admin event for now).
- `/admin/` conflict resolution if ginxsom truly hardcodes that path.
- fips-gateway service (disabled; users can enable manually for LAN translation).
- Tor transport for fips (transport is available but not wired into the config).
- Installer variant (live-only for now).
- arm64 build.
- Signed / reproducible builds.
- Hardening tests: SELinux, AppArmor profiles.
---
## 10. References
- [`plans/iso_architecture.md`](./iso_architecture.md) — overall target design
- [`plans/network_boot.md`](./network_boot.md) — sibling plan, paused
- [`build.sh`](../build.sh) / [`lb-wrapper.sh`](../lb-wrapper.sh) — build entry
- [`iso/auto/config`](../iso/auto/config) — live-build options
- [`includes/fips/packaging/debian/build-deb.sh`](../includes/fips/packaging/debian/build-deb.sh)
- [`includes/fips/packaging/common/fips.yaml`](../includes/fips/packaging/common/fips.yaml)
- [`includes/fips/packaging/debian/fips.service`](../includes/fips/packaging/debian/fips.service)
- [`includes/c-relay/systemd/c-relay.service`](../includes/c-relay/systemd/c-relay.service)
- [`includes/c-relay/README.md`](../includes/c-relay/README.md)
- [`includes/c-relay/API.md`](../includes/c-relay/API.md)
- [Live-build manual — includes.chroot_before_packages](https://live-team.pages.debian.net/live-manual/html/live-manual/customizing-contents.en.html)

157
plans/threat_model.md Normal file
View File

@@ -0,0 +1,157 @@
# n-OS-tr Threat Model and Privacy Contract
> **Status:** Draft. This document is the authoritative definition of what privacy properties n-OS-tr promises its users. Every future feature, package, service, and configuration change **must** be checked against this document. If a feature would violate a guarantee listed here, the feature does not ship. If a subsystem cannot be implemented without violating a guarantee, the guarantee wins.
>
> Scope: this document covers the *core* of n-OS-tr — the boot flow, identity subsystem, config projection, and the ephemeral rootfs contract. It does not attempt to extend privacy guarantees to *arbitrary user-installed software* beyond what the OS can enforce at the boundary.
## 1. What n-OS-tr is trying to protect
The single-sentence product promise is:
> After you power off an n-OS-tr machine, it should be indistinguishable — to any reasonable examiner with access to the machine and its boot media afterwards — from a machine that has never been booted at all.
That sentence implies several concrete properties:
- **No persistent local identity.** The machine does not retain your npub, nsec, or any derived key after shutdown.
- **No persistent local state.** Dotfiles, preferences, history, caches, cookies, and credentials do not survive shutdown.
- **No persistent cross-boot fingerprint.** `machine-id`, MAC address, hostname, and similar identifiers do not carry information between boots.
- **No secondary leakage.** Logs, swap, crash dumps, and journald do not contain your identity or activity after shutdown.
- **Identity portability.** The only source of truth for "who you are" is the 12-word mnemonic you hold externally (in memory, on paper, etc.). Reconstructing your environment on any other n-OS-tr machine produces the same identity, keys, and configuration.
## 2. Attacker model
This document defines which adversaries n-OS-tr attempts to defend against. Attackers not listed here are explicitly out of scope.
### 2.1 Adversaries we defend against
| Adversary | Scenario | Defense |
|-------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
| **Subsequent user of the same machine** | Someone boots the same laptop/PC after you and inspects local storage. | Root is tmpfs overlay. Nothing was written to the disk. |
| **Forensic analysis of the boot media** | Someone obtains the USB/SD after shutdown and mounts it read-write on their own machine. | Boot media is read-only during operation. Identity is not on it. |
| **Relay operator passively collecting events** | A relay operator sees all events you publish or fetch. | Private config is NIP-44-encrypted to yourself; only metadata is visible. |
| **Passive local network observer** | Someone watching the local Wi-Fi / Ethernet segment. | fips mesh + TLS for local services. Local relay preferred over remote. |
| **Hostile Wi-Fi / captive portal** | A network that tries to fingerprint, MITM, or redirect your traffic. | MAC randomization, no credential caching, DoH/DoT for upstream DNS. |
| **Accidental self-doxxing via shell history / logs** | You type your nsec into a terminal for debugging, or a service logs a key. | Volatile journald, `HISTFILE=/dev/null` by default, hardened stdout/stderr handling. |
| **Cross-machine correlation via fingerprints** | An observer tries to correlate two sessions on different machines as "the same user." | Fresh `machine-id`, fresh MAC, fresh hostname, no persistent UUIDs. |
### 2.2 Adversaries we do **not** defend against
These are listed explicitly so that we don't pretend. Users relying on n-OS-tr for privacy should understand these limits.
| Non-defense | Why not |
|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| **Compromised hardware with a hypervisor, TPM attack, or firmware implant** | We cannot defend a user whose laptop has been physically modified or whose firmware runs hostile code. |
| **Adversary physically present while the machine is booted** | If RAM contents and display can be captured live, the session's keys and plaintext are readable. |
| **Screen/keylogger implanted on the laptop** | Out of scope. We cannot enforce the integrity of I/O peripherals. |
| **Correlation by content / writing style** | Posting the same text from two different keys leaks the linkage. Stylometric defenses are out of scope. |
| **Relay operator collusion across many relays** | If every relay you use colludes, metadata correlation (timing, sizes, IPs) is possible. |
| **RF side channels, TEMPEST, acoustic attacks** | Out of scope. |
| **Nation-state adversary with global passive capability** | We aim for "meaningfully private against reasonable adversaries," not "NSA-proof." |
| **User leaking their own mnemonic** | If the user writes the 12 words on a sticky note attached to the monitor, no technical defense helps. |
| **Browser/app fingerprinting performed by software the user runs** | We provide a clean environment; we do not intercept or rewrite traffic from arbitrary user apps. |
## 3. Trust boundaries
Every subsystem in n-OS-tr trusts specific inputs and distrusts others. Listing them explicitly helps avoid implicit trust creep.
| Subsystem | Trusts | Does not trust |
|------------------------|--------------------------------------------------------------------|---------------------------------------------------------------|
| **boot loader + kernel** | The ISO/IMG that was flashed. Verified at build time by hash. | Anything on removable media other than the boot media itself. |
| **privacy-hardener** | Its own unit ordering, sysctl config, hook scripts baked into image. | Anything read from the network during or after hardening. |
| **identity-agent** | Keyboard input from the user; an imported mnemonic file if explicitly selected. | Files on persistent media by default. Network input. Any other process except its own supervised children. |
| **config-loader** | Events signed by the user's main key at NIP-06 index 0. | Events from any other key. Relay operators. Event metadata is not assumed confidential. |
| **config-writer** | User-initiated file changes; explicit "publish on shutdown" opt-in. | Relay uptime; concurrent edits from other clients without version tags. |
| **shutdown-wiper** | Its own deterministic cleanup script. | External signals ("clean shutdown") that cannot be verified. |
| **user services** (fips, c-relay, ginxsom, nginx) | Keys derived by identity-agent at fixed indices. Local network namespace. | Each other. Run with minimum privileges, distinct users, and hardened systemd units. |
## 4. Forbidden behaviors
This is the acceptance-criteria list. Every release must satisfy all of these. Deviations require a documented exception in this file, not a silent change.
### 4.1 The mnemonic and derived keys
- **F1.** The 12-word mnemonic MUST NOT be written to any block-backed storage (ext4, FAT, SD/USB block device) at any time.
- **F2.** The mnemonic MUST NOT be written to swap. Swap MUST be disabled by default.
- **F3.** Derived private keys (indices 0..N) MUST reside only in the identity-agent's process memory. They MUST NOT be persisted to disk.
- **F4.** Memory holding the mnemonic or any derived private key MUST be `mlock()`-ed (or equivalent) so that it cannot be paged out.
- **F5.** The shell MUST NOT record commands to a file. `HISTFILE=/dev/null` is the default; no `~/.bash_history`, no `~/.zsh_history`.
- **F6.** No service and no library may log or echo a private key, even at debug levels. Tests in the CI pipeline grep the shipped image for `nsec1` / `priv` patterns in logs and fail the build if any are found. *(CI rule to add — not yet present.)*
- **F7.** On clean shutdown, identity-agent MUST zero the memory regions holding the mnemonic and derived keys.
### 4.2 On-disk state
- **F8.** The root filesystem MUST be an overlayfs with a `tmpfs` upper layer. Writes MUST NOT reach the boot media during normal operation.
- **F9.** `/var/log`, `/var/lib`, `/home`, `/tmp`, `/run` MUST be tmpfs-backed.
- **F10.** systemd-journald MUST be configured `Storage=volatile` with RAM cap. No on-disk journal persistence is permitted by default.
- **F11.** The image MUST NOT ship with `rsyslog` or any syslog daemon that writes to `/var/log/*.log`.
- **F12.** Bash/zsh histories, `.viminfo`, `.lesshst`, and similar per-user history files are suppressed or redirected to `/dev/null` via shell profile defaults.
- **F13.** APT, pip, npm, cargo caches are either disabled or tmpfs-backed; they MUST NOT persist to the boot media.
### 4.3 Cross-boot fingerprints
- **F14.** `/etc/machine-id` MUST be regenerated at boot (e.g., empty at image-build time, filled by `systemd-machine-id-setup` on first boot of each session). It MUST NOT be the same across two boots.
- **F15.** MAC addresses on all network interfaces MUST be randomized at each boot by default. Opt-out available per-interface via a user-signed config event, not via persistent local state.
- **F16.** Hostname is set to a randomized or generic default; no user-chosen hostname persists unless explicitly set via Nostr config (and therefore tied to the mnemonic, not the machine).
- **F17.** NetworkManager / `wpa_supplicant` MUST NOT cache Wi-Fi credentials, SSIDs, or PSKs to the boot media. Wi-Fi credentials, if stored at all, live on Nostr (NIP-44 encrypted) and are scoped to the user's identity, not the machine.
- **F18.** Bluetooth pairing keys (`/var/lib/bluetooth/*`) MUST NOT persist.
- **F19.** The Linux kernel random number generator SHOULD be seeded from hardware RNG plus `getrandom()` at boot. No `/var/lib/systemd/random-seed` on the boot media.
### 4.4 Networking
- **F20.** DNS resolution SHOULD default to DNS-over-TLS or DNS-over-HTTPS to prevent local operator snooping. Fallback to plain DNS only when explicitly permitted by user config.
- **F21.** No service SHOULD bind on 0.0.0.0 / `::` by default unless explicitly required (e.g., fips ethernet transport listens on 0.0.0.0 by design, relay admin does not).
- **F22.** There SHOULD be a clearly documented way to operate fully offline — with network disabled — while still loading cached Nostr config from a local c-relay or local file.
### 4.5 Shutdown
- **F23.** Shutdown MUST run a wiper hook that:
- Kills the identity-agent (triggering its own memory-zero routine),
- Flushes/zeros known sensitive memory ranges (e.g., `/run/ginxsom/*.sock`, `/run/fips/*`),
- Unmounts tmpfs filesystems before the journald shutdown message is emitted.
- **F24.** Shutdown MUST NOT write any new files to the boot media.
- **F25.** Emergency shutdown (power loss, crash) MUST still leave the boot media indistinguishable from its pre-boot state, because nothing was ever written during normal operation (F8).
### 4.6 Image build and reproducibility
- **F26.** The image build MUST be deterministic given a fixed commit hash of this repo and its submodules. Two builds from the same inputs produce byte-identical ISOs (modulo embedded timestamps, which are pinned via `SOURCE_DATE_EPOCH`).
- **F27.** No built-in user account may have a password. If a `live` user is created by live-build, it is configured for auto-login on TTY only, with NOPASSWD sudo explicitly scoped (or disabled entirely in favor of root TTY + identity-agent-gated sudo).
- **F28.** The image MUST NOT phone home at build time or at runtime for telemetry, update checks, or crash reports. Zero outbound connections initiated by the OS itself (as distinct from services explicitly started by the user).
- **F29.** The image MUST NOT ship SSH host keys. If SSH is enabled, host keys are generated at first boot and exist only in tmpfs; reboot yields fresh host keys.
- **F30.** Every n-OS-tr C binary we ship (identity-agent, TUI, config-loader, config-writer, smoketest helpers, anything we write ourselves) MUST be **C99, built inside Alpine Docker, statically linked against musl libc**. `ldd <binary>` on a shipped artifact MUST print "not a dynamic executable". Rationale: smaller runtime trust surface (no `.so` resolution at runtime), identical behavior across amd64 and arm64 flavors, and alignment with [`includes/nostr_core_lib`](../includes/nostr_core_lib/) / [`includes/ginxsom`](../includes/ginxsom/) / [`includes/c-relay`](../includes/c-relay/). See [`plans/tui_login.md §0.1`](tui_login.md#01-toolchain-alpine--musl-dev-built-in-docker).
- **F31.** No Python, Node.js, or other interpreter runtime may be installed in the image on the critical boot path (identity entry, config projection, privacy-hardener). Interpreters MAY be available as user-installed software after a session is live, but the OS itself boots without them.
## 5. Shutdown contract
On `systemctl poweroff`, the following MUST hold before the kernel halts:
| Invariant | Enforcement |
|--------------------------------------------------------------------|-------------------------------------------------|
| No plaintext mnemonic in any mapped memory page | identity-agent zero-on-exit + shutdown-wiper |
| No derived private key in any mapped memory page | identity-agent zero-on-exit |
| No writes to the boot media since boot | Read-only mount + overlayfs topology (F8) |
| No journal entries written to disk | `Storage=volatile` (F10) |
| No residual Unix sockets with auth material in `/run/*` | shutdown-wiper removes + zeroes |
| No dumpable core files, no crash reports queued | `ulimit -c 0` default + no `systemd-coredump` on disk |
| No Wi-Fi state files in NM profiles / `/etc/NetworkManager` | Verified by CI grep (F17) |
## 6. Out-of-scope but worth documenting
- **The mnemonic input channel.** On the amd64 ISO, the user types the phrase into the keyboard attached to the machine. A keylogger on that machine defeats this. On the Pi Zero 2 W flavor, entry may be via the Waveshare LCD HAT buttons, reducing (not eliminating) shoulder-surfing risk. Neither is a substitute for physical security.
- **Randomness quality.** If a machine has no hardware RNG and a broken `/dev/urandom` seed, BIP-39 *generation* (not derivation) could produce weak mnemonics. Recommendation: use `nostr-id generate --dice` or an external hardware source when generating new identities on machines you don't fully trust.
- **Software supply chain.** We inherit Debian's package supply chain. A compromised upstream package compromises the image. Reproducible builds (F26) partially mitigate this but do not eliminate it.
## 7. What this document is **not**
- Not a design doc for *how* each property is enforced. See [`plans/identity_subsystem.md`](identity_subsystem.md), [`plans/nostr_config_projection.md`](nostr_config_projection.md), and future `plans/privacy_hardener.md` for mechanism-level detail.
- Not a conformance checklist for applications the user installs themselves. A user running Firefox can still be fingerprinted by Firefox — that's Firefox's problem, not the OS's.
- Not a promise that the current Phase 2 Slice B+C ISO already satisfies everything here. Most of these guarantees become real in **Phase 3** (identity + config + hardener). Phase 2 is the plumbing; Phase 3 is the promise.
## 8. Revision policy
Any PR that weakens a guarantee (removes an F-item, adds an exception, broadens an allowed behavior) must:
1. Update this document to reflect the new guarantee.
2. Update the corresponding smoke-test check, so behavior drift is caught by CI.
3. Include an explicit rationale in the PR description.
Any PR that strengthens a guarantee is always welcome and should update this document to match.

508
plans/tui_login.md Normal file
View File

@@ -0,0 +1,508 @@
# TUI Login — Boot-Time Identity Entry
> **Status:** Design. This document specifies the n-OS-tr boot-time TUI (`nostr-id-tui`): a pure-C99 ncurses program that owns `tty1` before any login prompt, lets the user generate/enter/import a 12-word BIP-39 mnemonic, hands the mnemonic to the identity-agent, and exits.
>
> Depends on: [`plans/identity_subsystem.md`](identity_subsystem.md) (the daemon the TUI talks to), [`plans/threat_model.md`](threat_model.md) (memory-hygiene constraints F1F7), [`includes/nostr_core_lib`](../includes/nostr_core_lib/) (all Nostr crypto).
## 0. Rules the doc follows (and everything in this repo follows)
1. **C99 only.** `-std=c99 -Wall -Wextra -Wpedantic`. No Python, no Node.js at runtime, no Rust, no C++. Same rule as [`nostr_core_lib`](../includes/nostr_core_lib/). This keeps the image small, static, reproducible, and portable to Pi Zero class hardware.
2. **Nostr crypto and BIP-39 primitives come from `nostr_core_lib`.** If we need a new primitive and it's reusable outside n-OS-tr, we **upstream it into `nostr_core_lib`**, not add it here.
3. **Policy, systemd wiring, and product-specific UX live in this repo.** Role table, screen flow, service units, live-build hooks — all here.
4. **No on-disk state** ever, per [threat_model.md F1F13](threat_model.md#4-forbidden-behaviors).
5. **Statically linked against musl libc.** Same rule as [`includes/nostr_core_lib`](../includes/nostr_core_lib/). See §0.1 for the toolchain and rationale. Output is one fully static ELF with no runtime `.so` dependencies — not glibc, not `libncurses.so`, not `libssl.so`. The binary runs on any Linux kernel of compatible architecture without caring about the host's userspace.
## 0.1 Toolchain: Alpine + musl-dev, built in Docker
Every C99 binary we produce — `nostr-id-tui`, the identity-agent, `nostr-id` CLI, config-loader, config-writer — follows the same build pattern that [`includes/ginxsom`](../includes/ginxsom/) and [`includes/c-relay`](../includes/c-relay/) already use:
- **Build inside Alpine Linux** ([`alpine:3.19`](https://alpinelinux.org/)) via a Dockerfile named `Dockerfile.alpine-musl`.
- **Install dependencies as musl-dev packages** via `apk add`, using the `-static` variants where required (e.g. `ncurses-static`, `openssl-libs-static`, `zlib-static`).
- **Compile with `gcc -std=c99 -static`** against [`libnostr_core.a`](../includes/nostr_core_lib/).
- **Output is `<name>_static_<arch>`** — the same naming convention as [`c_relay_static_x86_64`](../includes/c_relay_static_x86_64), `ginxsom-fcgi_static_x86_64`, `ginxsom-fcgi_static_arm64` (see [`includes/ginxsom/build_static.sh:66`](../includes/ginxsom/build_static.sh:66)).
- **Wrapper script `build_static.sh`** in each subtree drives the Docker build, handles `uname -m``linux/amd64` | `linux/arm64` platform detection, and extracts the binary into `build/`.
Why this matters, beyond matching what already works:
- **Zero glibc entanglement.** We never ask "which Debian version, which glibc?" The binary is self-contained.
- **Same binary runs on amd64 ISO and on Pi Zero 2 W arm64.** We build both flavors with `docker build --platform linux/arm64 ...` and get functionally identical behavior, which is essential for the "one product, two image flavors" story.
- **No version drift with [`nostr_core_lib`](../includes/nostr_core_lib/).** Building in the same Alpine container the library itself is tested against eliminates an entire class of "worked on my machine" bugs.
- **Threat-model alignment.** A dynamically linked binary's runtime behavior depends on whatever `.so` files happen to be on the host when it runs. A static musl binary doesn't. That's a smaller trust surface, which matches [`threat_model.md §4.6 F26`](threat_model.md#46-image-build-and-reproducibility) "deterministic build."
### 0.1.1 Concrete toolchain recipe
```dockerfile
# Dockerfile.alpine-musl for any n-OS-tr C binary
FROM alpine:3.19 AS builder
RUN apk add --no-cache \
build-base \
musl-dev \
git \
cmake \
autoconf \
automake \
libtool \
pkgconf \
# nostr_core_lib deps
openssl-dev openssl-libs-static \
zlib-dev zlib-static \
curl-dev curl-static \
libsecp256k1-dev \
# tui-specific
ncurses-dev ncurses-static
WORKDIR /src
COPY . /src
# Build libnostr_core.a (via its own build.sh)
RUN cd includes/nostr_core_lib && ./build.sh x64
# Build the TUI against it
RUN gcc -std=c99 -Wall -Wextra -Wpedantic -static \
-I includes/nostr_core_lib/nostr_core \
-I includes/nostr_core_lib/cjson \
stack/nostr-id-tui/src/*.c \
includes/nostr_core_lib/libnostr_core_x64.a \
-lncurses -ltinfo -lssl -lcrypto -lcurl -lsecp256k1 -lz -lm \
-o /out/nostr-id-tui_static_x86_64
```
### 0.1.2 ncurses terminfo: pick a lane in v1
Even a statically linked ncurses binary loads terminfo entries from disk at runtime (`/usr/share/terminfo/l/linux`, etc.). Two options for v1:
| Option | Pros | Cons | Verdict |
|---|---|---|---|
| **Ship `ncurses-term` in the ISO.** Add to [`base.list.chroot`](../iso/config/package-lists/base.list.chroot). | Simplest. ~2 MB. Works with every `TERM` value. | Terminfo files on disk. | **v1 default.** |
| **Compile in fallback entries.** Use `ncurses --enable-termcap --with-fallbacks=linux,vt102,vt220` so those entries are baked into the static binary. | Binary is fully self-contained. | Needs ncurses rebuild + more build-time config. | Size-optimization pass for later. |
For v1 we go Option 1; it's compatible, ships in days, and the size cost is negligible. Revisit Option 2 if we ever want truly zero-filesystem-read boot behavior.
## 1. What the TUI does
On a clean boot of an n-OS-tr machine, after the privacy-hardener has run but before any login prompt is displayed:
1. Take over `tty1`.
2. Present four options: **Enter 12 words**, **Generate new identity**, **Import from removable media**, **Connect to a remote signer (NIP-46 bunker)**.
3. Drive the chosen flow to completion.
4. Hand the resulting mnemonic (or the bunker URL) to `identity-agent` via a local Unix socket.
5. Exit cleanly, allowing systemd to start `getty@tty1` with the identity already loaded.
After the TUI exits, all downstream services (c-relay, fips, ginxsom, config-loader) come up against a deterministic identity derived from the user's mnemonic. See [`identity_subsystem.md` §3](identity_subsystem.md#3-boot-time-ux) for the broader flow diagram.
## 2. Ownership and placement
### 2.1 Code that lives in `nostr_core_lib` (upstream additions)
The TUI design requires three small, genuinely generic utility modules. These are **new upstream contributions to [`nostr_core_lib`](../includes/nostr_core_lib/)**. They are not n-OS-tr-specific; any C Nostr app that needs to handle a mnemonic or a private key will want them.
#### 2.1.1 `nostr_core/nostr_secure_memory.h`
Memory-locked, zero-on-drop byte buffers for secrets.
```c
/* Locked byte buffer. Contents are mlock()-ed; destroy() explicit_bzero()'s
* before unlocking. Use for mnemonics, private keys, decrypted plaintext. */
typedef struct nostr_secure_buf {
uint8_t *bytes;
size_t len;
size_t cap; /* rounded up to a page for mlock */
} nostr_secure_buf_t;
int nostr_secure_buf_init(nostr_secure_buf_t *buf, size_t cap); /* mmap + mlock */
void nostr_secure_buf_destroy(nostr_secure_buf_t *buf); /* bzero + munlock + munmap */
int nostr_secure_buf_append(nostr_secure_buf_t *buf, const void *src, size_t n);
void nostr_secure_buf_reset(nostr_secure_buf_t *buf); /* bzero, keep cap */
/* Also: process-level hardening helpers */
int nostr_disable_core_dumps(void); /* prctl(PR_SET_DUMPABLE, 0) + RLIMIT_CORE=0 */
int nostr_lock_all_memory(void); /* mlockall(MCL_CURRENT|MCL_FUTURE) */
```
Implementation uses `mlock(2)` + `explicit_bzero(3)` on Linux, and a documented best-effort fallback on macOS. `nostr_core_lib` already runs as C99 on both platforms; this fits cleanly.
#### 2.1.2 `nostr_core/nostr_bip39_ui.h`
Headless helpers for building any mnemonic entry UI (ncurses, GTK, web, LCD — anything).
```c
/* Given a typed prefix (lowercase, 1-8 chars), fill `out[]` with up to
* `max` candidate word pointers from the BIP-39 English wordlist. Returns
* the number filled. */
int nostr_bip39_prefix_candidates(const char *prefix,
const char **out,
int max);
/* Quick validity check — returns 1 if `word` is in the BIP-39 English list. */
int nostr_bip39_is_word(const char *word);
/* Validate a full mnemonic's checksum. Returns NOSTR_SUCCESS iff the
* mnemonic parses and its checksum is correct. Does NOT derive any keys. */
int nostr_bip39_validate(const char *mnemonic_space_separated);
/* For the "confirm you wrote it down" flow:
* Given a mnemonic and a count (e.g., 3), fill `idx_out[]` with that
* many distinct random word positions (0..11 for a 12-word mnemonic),
* and `word_out[]` with pointers into the mnemonic for those positions.
* Caller provides the RNG as a simple callback for determinism in tests. */
typedef int (*nostr_rand_u32_fn)(void *user, uint32_t *out);
int nostr_bip39_random_confirm_positions(const char *mnemonic,
int count,
int *idx_out,
const char *(*word_out)[],
nostr_rand_u32_fn rng,
void *rng_user);
```
The English wordlist is already present inside `nostr_core_lib`'s NIP-06 implementation; we just expose it.
#### 2.1.3 `nostr_core/nostr_fips_ipv6.h`
Single utility function so every component that wants to know "what's the fips IPv6 for this pubkey" uses the same canonical computation.
```c
/* Compute the fips mesh IPv6 address for a 32-byte Nostr pubkey.
* Output buffer must be at least 40 bytes (colon-hex ipv6 fits in 39+NUL).
* Matches tools.html pubkeyHexToFipsIpv6() exactly. */
int nostr_fips_ipv6_from_pubkey(const uint8_t pubkey[32],
char *out,
size_t out_len);
```
### 2.2 Code that lives in this repo (`n_os_tr`)
```
stack/nostr-id-tui/ # new subtree
├── CMakeLists.txt # C99 build, links libnostr_core.a + libncurses.a
├── include/
│ └── n_os_tr_tui.h # internal header
├── src/
│ ├── main.c # entry point, arg parsing, mode dispatch
│ ├── screen_menu.c # main menu (enter / generate / import / bunker)
│ ├── screen_enter.c # 12-word entry with per-word autocomplete
│ ├── screen_generate.c # generate + display + 3-position confirm
│ ├── screen_import.c # /media/*/mnemonic.txt discovery + wipe
│ ├── screen_bunker.c # NIP-46 bunker URL entry
│ ├── screen_error.c # fatal error screen
│ ├── curses_helpers.c # small ncurses wrappers (input w/o history)
│ ├── agent_client.c # speak nostr-id control socket (see §5)
│ └── memhygiene.c # thin wrappers over nostr_secure_memory
└── tests/
├── test_screen_enter.c # feeds canned keystrokes, asserts transitions
├── test_screen_generate.c
├── test_agent_client.c # mock control-socket server
└── test_integration.sh # qemu serial-console scripted boot
```
Also in this repo:
- `iso/config/includes.chroot/etc/systemd/system/nostr-id-tui.service` — the unit that owns `tty1`.
- `iso/config/includes.chroot/etc/systemd/system/getty@tty1.service.d/nostr-id-tui-ordering.conf` — delays getty until TUI exits.
- `iso/config/hooks/live/0030-mask-getty-tty1-until-identity.hook.chroot` — masks `getty@tty1` during TUI flow.
- Build-system glue in `build.sh` to stage the compiled binary into the chroot.
## 3. Screen flow
### 3.1 State diagram
```mermaid
stateDiagram-v2
[*] --> Menu
Menu --> Enter: 1 / E
Menu --> Generate: 2 / G
Menu --> Import: 3 / I
Menu --> Bunker: 4 / B
Menu --> Amnesia: 5 / A (advanced)
Enter --> EnterValid: BIP-39 checksum ok
Enter --> Enter: checksum fail, clear buffer, retry
EnterValid --> SendAgent
Generate --> ShowWords
ShowWords --> ConfirmPositions: user presses Continue
ConfirmPositions --> SendAgent: user correctly retypes 3 words
ConfirmPositions --> ShowWords: wrong, re-display
Import --> ImportConfirm: file found
Import --> Menu: no file found (error)
ImportConfirm --> WipeSource: user confirms
WipeSource --> SendAgent
Bunker --> BunkerConnect: URL parsed
BunkerConnect --> SendAgent: NIP-46 connect ok
BunkerConnect --> Bunker: connect failed, retry
Amnesia --> SendAgentAmnesia
SendAgent --> Exit: agent returns ok
SendAgent --> FatalError: agent returns error
SendAgentAmnesia --> Exit
FatalError --> [*]
Exit --> [*]
```
### 3.2 Each screen in detail
**Menu.** Four options (plus an advanced amnesia mode behind a keystroke), plus version/build info and a "Ctrl-L to redraw" hint. Keyboard-only; mouse not required.
**Enter.** A grid of 12 slots (e.g. 4 rows × 3 cols). The cursor sits on slot 1. The user types a prefix; autocomplete offers candidate words below (using [`nostr_bip39_prefix_candidates`](../includes/nostr_core_lib/nostr_core/)). Tab completes to the longest common prefix or the single candidate. Space or Enter commits the current slot and moves to the next. Backspace edits the current slot. When all 12 slots are filled, `Ctrl-Enter` (or menu → confirm) validates via [`nostr_bip39_validate`](../includes/nostr_core_lib/nostr_core/). Invalid checksum → clear all 12 slots and show "checksum invalid, try again".
**Generate.** Calls [`nostr_generate_mnemonic_and_keys`](../includes/nostr_core_lib/nostr_core/nip006.h:21). Shows all 12 words in a 3-row × 4-column grid, numbered 1..12. Big red banner: "WRITE THESE WORDS DOWN NOW. They will not be shown again." User presses Continue.
**Confirm positions.** Calls [`nostr_bip39_random_confirm_positions`](../includes/nostr_core_lib/nostr_core/) to pick 3 random positions. Asks the user to retype the word at each position. Any mismatch resets to the generate screen (they clearly didn't record it). All three correct → commit.
**Import.** Scans `/media/*/mnemonic.txt`. If a single candidate is found, prompts: "Mnemonic file found at `<path>`. Accept and securely wipe the file? (y/N)". User confirms → read the file into a secure buffer → validate via [`nostr_bip39_validate`](../includes/nostr_core_lib/nostr_core/) → open the file, zero it, truncate, `fsync`, unlink, `fsync` the dirfd → commit.
**Bunker.** A single text field for a `bunker://` URL (NIP-46). On submit, parse via [`nostr_nip46_parse_bunker_url`](../includes/nostr_core_lib/nostr_core/nip046.h:101) and attempt [`nostr_nip46_client_session_init`](../includes/nostr_core_lib/nostr_core/nip046.h:145) + [`nostr_nip46_client_ping`](../includes/nostr_core_lib/nostr_core/nip046.h:156). On success, send the session to the agent; on failure, show the error and return to the bunker URL input.
**Amnesia.** No persistence, no Nostr config projection, ephemeral main key generated via [`nostr_generate_keypair`](../includes/nostr_core_lib/nostr_core/nip006.h:20). Big red banner: "AMNESIA MODE. This identity dies at shutdown."
**FatalError.** A single full-screen error. No recovery — require the user to reboot. This is only reached if ncurses init failed, the agent control socket is unavailable, or memory locking failed (which means the threat-model invariants cannot be enforced).
### 3.3 Anti-shoulder-surf tradeoffs
- During **Enter** flow, the current word being typed is visible (for usability). Previously committed words display as `****` — the full 12 are never visible together.
- During **Generate**, all 12 words are visible simultaneously because the user must copy them down. This is the inverse tradeoff and we accept it.
- During **Confirm**, only three words are ever on screen. Retyped words are not echoed with autocomplete (the user must actually know their phrase).
## 4. Memory hygiene
All bullet points trace back to [threat_model.md §4.1](threat_model.md#41-the-mnemonic-and-derived-keys).
| Invariant | Implementation |
|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
| No disk storage of the mnemonic | All buffers are `nostr_secure_buf_t` (§2.1.1). Nothing goes to `stdio.h` file streams. |
| No swap | Process calls `nostr_lock_all_memory()` at startup. Image has swap disabled image-wide anyway (F2). |
| No core dump | `nostr_disable_core_dumps()` at startup. |
| Shell history suppression | TUI is exec'd by systemd, not a shell. Not applicable here. |
| No ncurses internal leaks | Instead of `wgetstr()`/`wgetnstr()`, use `wgetch()` in a loop into our own `nostr_secure_buf_t`. Never pass the mnemonic into any ncurses buffer. |
| `TIOCSTI` history / paste bracketing | Disable line-mode: `cbreak()` + `noecho()` set once on startup. |
| No dumped signals | Install `SIGQUIT` / `SIGBUS` / `SIGSEGV` handlers that zero all secure buffers before `_exit()`. |
| Exit wipe | `atexit(3)` handler zeros secure buffers and calls `endwin()` cleanly. |
| Zero on success path too | After handing the mnemonic to the agent, the TUI's own copy is zeroed before `exit(0)`. |
## 5. Control socket protocol
The TUI talks to `identity-agent` over a single Unix domain socket:
```
/run/nostr-id/control.sock
```
Same socket path as the rest of `nostr-id` (see [identity_subsystem.md §5.1](identity_subsystem.md#51-cli-nostr-id)).
Wire format: one JSON object per request followed by one JSON object response, terminated by `\n`. The JSON doc is small enough that we can use a tiny hand-written parser or [`cjson`](../includes/nostr_core_lib/cjson/) which is already vendored in `nostr_core_lib`.
### 5.1 Methods used by the TUI
**`load_mnemonic`** — primary.
```json
// request
{"op":"load_mnemonic","mnemonic":"<12 words space-separated>"}
// response
{"ok":true,"main_npub":"npub1..."}
// or
{"ok":false,"error":"checksum_invalid"}
```
The agent validates, derives all declared role keys, holds them in `mlock`-ed memory, and returns the main npub for display. The mnemonic string is zeroed in the wire buffer after the agent acks.
**`load_bunker`** — external signer flow.
```json
{"op":"load_bunker","url":"bunker://<pubkey>?relay=...&secret=..."}
{"ok":true,"main_npub":"npub1..."}
```
**`load_amnesia`** — ephemeral mode.
```json
{"op":"load_amnesia"}
{"ok":true,"main_npub":"npub1..."}
```
**`status`** — TUI health check, safe to call from anyone.
```json
{"op":"status"}
{"state":"awaiting_identity"|"loaded"|"shutdown"}
```
### 5.2 Authentication
The socket lives at `/run/nostr-id/control.sock` mode `0660`, owned by `root:nostr-signer`. The TUI process runs as the agent's dedicated service user (`nostr-id`) or as root on tty1 and is a member of `nostr-signer`. Any other process that wants to talk to the agent must be added to that group — governed per [identity_subsystem.md §4.3](identity_subsystem.md#43-processes-and-isolation).
No further auth: any process with socket access can load a mnemonic. The TUI lives on `tty1` before login; the only "process" that gets to speak on the socket before the user has typed a mnemonic is the TUI itself.
## 6. systemd integration
Three files land in the ISO chroot.
### 6.1 `etc/systemd/system/nostr-id-tui.service`
```ini
[Unit]
Description=n-OS-tr boot-time identity TUI
Documentation=file:///usr/share/doc/n-os-tr/README
After=nostr-id.service systemd-user-sessions.service
Wants=nostr-id.service
Before=getty@tty1.service
ConditionPathExists=!/var/lib/n-os-tr/.identity-loaded
[Service]
Type=oneshot
RemainAfterExit=no
ExecStart=/usr/local/sbin/nostr-id-tui
StandardInput=tty
StandardOutput=tty
StandardError=journal
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
TimeoutStartSec=infinity
# memory hygiene (mirrors the binary's runtime asserts)
LockPersonality=yes
NoNewPrivileges=yes
MemoryDenyWriteExecute=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
RestrictAddressFamilies=AF_UNIX
SystemCallArchitectures=native
CapabilityBoundingSet=CAP_IPC_LOCK
AmbientCapabilities=CAP_IPC_LOCK
[Install]
WantedBy=multi-user.target
```
### 6.2 `etc/systemd/system/getty@tty1.service.d/nostr-id-tui-ordering.conf`
```ini
[Unit]
# getty on tty1 must not race the identity TUI
After=nostr-id-tui.service
Requires=nostr-id-tui.service
```
### 6.3 `etc/systemd/system/nostr-id-tui.service.d/zz-override.conf`
For the Pi LCD variant (shipped only in the Pi image), this drop-in overrides `ExecStart` to launch a different binary that uses the LCD-button backend. Same control-socket protocol, different screen layer.
### 6.4 Flow
```mermaid
sequenceDiagram
participant SD as systemd
participant IA as nostr-id.service
participant TUI as nostr-id-tui.service
participant GT as getty@tty1.service
SD->>IA: start
IA-->>SD: active (listening on control.sock)
SD->>TUI: start (owns tty1)
Note over TUI: user interacts
TUI->>IA: load_mnemonic / load_bunker / load_amnesia
IA-->>TUI: ok
TUI->>SD: exit 0
SD->>GT: start (now allowed by ordering)
GT->>User: "n-os-tr login:"
```
## 7. Per-flavor abstraction
The TUI's screen logic is separated from its rendering layer so that the same state machines can drive different front-ends:
- `screen_*.c` files contain **state machines only**: given an input event, what's the next state?
- An `ui_backend_t` interface abstracts drawing: `draw_menu(choices)`, `draw_text_grid(words)`, `read_input()`, `redraw()`, etc.
- For amd64 we implement `ui_backend_curses.c` using ncurses.
- For the Pi flavor, a future `ui_backend_lcd.c` implements the same interface over the [Waveshare 1.3" LCD HAT](../../raspberry_pi_zero_nostr/plans/waveshare_1.3inch_lcd_hat.md). Same state machines, same tests — different backend.
This is the single biggest design choice of the TUI: **no screen file may call ncurses directly.** All rendering goes through the backend interface. This is why the Pi LCD port later will be a pure rendering change, not a logic rewrite.
## 8. Test plan
### 8.1 Unit (C, built alongside the binary)
- `test_screen_enter` — feeds a scripted sequence of keystrokes through the enter state machine with a mock backend; asserts the final mnemonic buffer state and agent-client invocation.
- `test_screen_generate` — with a deterministic RNG (via `nostr_rand_u32_fn` callback), asserts that the generated mnemonic is valid per [`nostr_bip39_validate`](../includes/nostr_core_lib/nostr_core/) and that the confirm-positions flow accepts the correct three words and rejects wrong ones.
- `test_agent_client` — runs a local mock control-socket server; asserts request/response formatting, timeout, error propagation.
### 8.2 Integration (VM, scripted)
`tests/test_integration.sh` drives a QEMU VM via serial console:
1. Build the ISO with a baked-in test mode flag that replaces `tty1` input with a scripted pty.
2. Boot the ISO in QEMU with `-nographic`.
3. Script feeds a canned 12-word mnemonic via expect.
4. Assert the TUI exits, the identity-agent reports the expected main npub over the control socket, and the smoke test reports all-green.
Test mnemonic used across the repo: a single well-known BIP-39 test vector (e.g. the Trezor test vector "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") whose derived keys are fixed and known. The smoke test hardcodes the expected npub for each of our declared roles under this mnemonic.
### 8.3 Non-goals
- We do not test ncurses itself. We test our backend interface and the state machines through a mock backend.
- We do not test the LCD backend until the Pi flavor ships. The interface guarantees that porting the tests is cheap.
## 9. Failure modes (summary)
| Failure | Behavior |
|---------------------------------------------|-----------------------------------------------------------------------------------------|
| Can't `mlockall` | Exit with FatalError. Threat-model invariants cannot be enforced. |
| ncurses init fails / bad `TERM` | Exit with FatalError on stderr; systemd keeps TUI inactive and tty1 remains claimed. |
| Agent control socket not present | Retry every 200 ms for 10 s; then FatalError. |
| Agent reports `checksum_invalid` | Clear Enter state, return to Enter screen with an error line. |
| User mistypes 3 confirm words | Return to generate screen (they haven't recorded it); do not retry with same words. |
| Bunker URL unreachable | Error line, return to bunker screen. |
| User mashes Ctrl-C | Ignore; the TUI is the only user-facing input path, and aborting means no identity. |
| Kernel signals (SIGTERM during shutdown) | Signal handler zeros buffers, calls `endwin()`, exits. |
## 10. Packaging and build
- **Build environment.** Alpine 3.19 + `musl-dev` inside Docker. See §0.1 for the toolchain recipe and the `Dockerfile.alpine-musl` template.
- **Source tree.** `stack/nostr-id-tui/` with:
- `Dockerfile.alpine-musl` — the actual builder image.
- `build_static.sh` — wrapper script that runs `docker build --platform linux/amd64|linux/arm64`, extracts the binary, and places it under `build/`.
- `CMakeLists.txt` (*optional*, used inside the Alpine container) — C99 targets, links [`libnostr_core.a`](../includes/nostr_core_lib/), pulls `libncurses.a`/`libtinfo.a` (static) and `libsecp256k1.a`/`libssl.a`/`libcrypto.a`/`libcurl.a`/`libz.a` from Alpine's `-static` packages.
- **Outputs.** `stack/nostr-id-tui/build/nostr-id-tui_static_x86_64` and `_static_arm64`. Fully static ELFs; `ldd` reports "not a dynamic executable."
- **Runtime ISO deps.**
- `ncurses-term` added to [`iso/config/package-lists/base.list.chroot`](../iso/config/package-lists/base.list.chroot) so terminfo files are on the image.
- No other runtime deps (no `libc.so`, no `libncurses.so`).
- **ISO integration.** [`build.sh`](../build.sh) gains a step that runs `stack/nostr-id-tui/build_static.sh` for the target arch, then stages the output binary into `iso/config/includes.chroot_before_packages/live-artifacts/nostr-id-tui`, following the same pattern as the [fips deb staging](slice_bc_fips_crelay.md#47-updated-buildsh). A [`hook.chroot`](../iso/config/hooks/live/) installs it to `/usr/local/sbin/nostr-id-tui` with mode 0755 and owner `root:nostr-signer`.
- **Reproducibility.** The Dockerfile pins `alpine:3.19` (a content-addressed tag). Given a fixed repo commit and a fixed base image digest, byte-identical output is expected.
## 11. What this doc does NOT cover
- The identity-agent daemon internals — see [`identity_subsystem.md`](identity_subsystem.md). This doc treats the agent as a black box reachable over `/run/nostr-id/control.sock`.
- The role → index schedule — see [`identity_subsystem.md §2.1`](identity_subsystem.md#21-the-role-table-schedule).
- Config projection after identity is loaded — see [`nostr_config_projection.md`](nostr_config_projection.md).
- GUI variants — out of scope until we decide on a GUI at all. The `ui_backend_t` abstraction leaves the door open for a GUI backend later, but no GUI is planned for v1.
## 12. Open questions
- **Should the TUI support serial console in v1?** A serial `TERM=vt100` works for ncurses but weakens the "physical keyboard only" stance. Suggest: off by default; opt-in via kernel cmdline for specialized deployments.
- **Where does `TERM` default come from?** systemd's `TTYPath=/dev/tty1` service sets `TERM=linux`; for serial we need `TERM=vt220`. Keep this in the service file, not the binary.
- **Do we want a "word list display" in the Generate screen that includes the index and checksum-friendly prefixes alongside the word?** Nice to have for users copying to paper. Leaves more on screen at once. Decide during UX polish.
- **Is 15/18/21/24-word BIP-39 input supported?** The [`identity_subsystem.md` open questions](identity_subsystem.md#9-open-questions) say 12 is default; longer phrases opt-in. TUI should accept all lengths if the user types more slots. Slot grid becomes a dynamic 3/4/5/6/8-row table accordingly. v1 can ship with 12-only and add more later.
## 13. Implementation order (Phase 3a.1)
Proposed checklist — small steps, each independently verifiable. Note the toolchain work lands first so everything built on top is musl-static from the beginning.
1. **Add the Alpine/musl builder for n-OS-tr C binaries.** Copy [`includes/ginxsom/Dockerfile.alpine-musl`](../includes/ginxsom/Dockerfile.alpine-musl) + [`includes/ginxsom/build_static.sh`](../includes/ginxsom/build_static.sh) as the template and land `stack/build-common/Dockerfile.alpine-musl` + `stack/build-common/build_static.sh` for reuse across nostr-id-tui, identity-agent, nostr-id CLI, config-loader, config-writer.
2. **Upstream `nostr_secure_memory` module into [`nostr_core_lib`](../includes/nostr_core_lib/).** Header + implementation + unit test. Verify it compiles cleanly in the Alpine/musl builder.
3. **Upstream `nostr_bip39_ui` module into [`nostr_core_lib`](../includes/nostr_core_lib/).** Header + implementation (using the existing wordlist) + unit test.
4. **Upstream `nostr_fips_ipv6` utility into [`nostr_core_lib`](../includes/nostr_core_lib/).** Header + implementation + unit test (matching [`tools.html`](../../client/www/tools.html:750)).
5. **Define the `ui_backend_t` interface in `stack/nostr-id-tui/include/`.**
6. **Implement state machines** (`screen_menu.c`, `screen_enter.c`, `screen_generate.c`). Run unit tests in the Alpine/musl builder with a mock backend before any ncurses work.
7. **Implement `ui_backend_curses.c`.** First time we pull `ncurses-dev`/`ncurses-static` into the Dockerfile.
8. **Implement `agent_client.c`** — Unix socket + cjson wire.
9. **Tie it together in `main.c`, bundle signal handlers, atexit wipes.** First successful build of a static `nostr-id-tui_static_x86_64` should happen here.
10. **Write the three systemd units + live-build hook.** Verify the staged binary lands at `/usr/local/sbin/nostr-id-tui` in the chroot.
11. **ISO build integration.** `build.sh` invokes `stack/nostr-id-tui/build_static.sh` and stages the resulting binary. Boot the ISO in QEMU; confirm the TUI comes up on `tty1`.
12. **VM integration test.** Script a QEMU serial-console boot that feeds the Trezor test vector mnemonic; assert the identity-agent reports the matching main npub.
13. **Smoketest extension.** Add TUI-related checks to [`n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) (binary is static, `ldd` says "not a dynamic executable", terminfo entries present).
14. **arm64 cross-build.** Add `docker build --platform linux/arm64 ...` to `build_static.sh` so the same source produces `nostr-id-tui_static_arm64` for the Pi flavor. Binary itself shouldn't require any changes — that's the point of the musl-static discipline.

View File

74
scripts/run-iso.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ISO_PATH="${ISO_PATH:-$REPO_ROOT/iso/live-image-amd64.hybrid.iso}"
HTTPS_PORT="${HTTPS_PORT:-2443}"
RELAY_PORT="${RELAY_PORT:-28888}"
CPUS="${CPUS:-2}"
MEM_MB="${MEM_MB:-2048}"
USE_UEFI="${USE_UEFI:-0}"
HEADLESS="${HEADLESS:-0}"
if [[ ! -f "$ISO_PATH" ]]; then
echo "[run-iso] missing ISO: $ISO_PATH" >&2
echo "[run-iso] build first: ./build.sh --clean" >&2
exit 1
fi
if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then
echo "[run-iso] missing qemu-system-x86_64. Install: sudo apt-get install -y qemu-system-x86 ovmf" >&2
exit 1
fi
QEMU_FW_ARGS=()
if [[ "$USE_UEFI" == "1" ]]; then
if [[ -f /usr/share/ovmf/OVMF.fd ]]; then
# Monolithic OVMF image (works with -bios)
QEMU_FW_ARGS=(-bios /usr/share/ovmf/OVMF.fd)
elif [[ -f /usr/share/OVMF/OVMF_CODE_4M.fd && -f /usr/share/OVMF/OVMF_VARS_4M.fd ]]; then
# Split OVMF image (CODE + writable VARS)
VARS_TMP="${TMPDIR:-/tmp}/n-os-tr-ovmf-vars-$$.fd"
cp /usr/share/OVMF/OVMF_VARS_4M.fd "$VARS_TMP"
QEMU_FW_ARGS=(
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd
-drive if=pflash,format=raw,file="$VARS_TMP"
)
else
echo "[run-iso] no usable OVMF firmware found" >&2
exit 1
fi
fi
ACCEL_ARGS=()
if [[ -r /dev/kvm && -w /dev/kvm ]]; then
ACCEL_ARGS=(-enable-kvm -machine q35,accel=kvm -cpu host)
else
echo "[run-iso] /dev/kvm not accessible; using TCG emulation" >&2
ACCEL_ARGS=(-machine q35,accel=tcg -cpu max)
fi
echo "[run-iso] booting: $ISO_PATH"
echo "[run-iso] firmware mode: $([[ "$USE_UEFI" == "1" ]] && echo uefi || echo bios)"
echo "[run-iso] host forwards: https://127.0.0.1:${HTTPS_PORT} -> guest:443, tcp://127.0.0.1:${RELAY_PORT} -> guest:8888"
EXTRA_DISPLAY_ARGS=()
if [[ "$HEADLESS" == "1" ]]; then
echo "[run-iso] mode: headless serial (Ctrl-a x to quit, Ctrl-a c monitor)"
EXTRA_DISPLAY_ARGS=(-nographic -serial mon:stdio)
else
echo "[run-iso] mode: graphical window"
EXTRA_DISPLAY_ARGS=()
fi
exec qemu-system-x86_64 \
"${ACCEL_ARGS[@]}" \
"${QEMU_FW_ARGS[@]}" \
-boot order=d,menu=off \
-smp "$CPUS" \
-m "$MEM_MB" \
-drive file="$ISO_PATH",format=raw,if=ide,media=cdrom,readonly=on \
-netdev user,id=n0,hostfwd=tcp::${HTTPS_PORT}-:443,hostfwd=tcp::${RELAY_PORT}-:8888 \
-device virtio-net-pci,netdev=n0 \
"${EXTRA_DISPLAY_ARGS[@]}"

View File

@@ -0,0 +1,32 @@
FROM alpine:3.19 AS base
RUN apk add --no-cache \
build-base \
musl-dev \
git \
cmake \
autoconf \
automake \
libtool \
pkgconf \
openssl-dev \
openssl-libs-static \
zlib-dev \
zlib-static \
curl-dev \
curl-static \
libsecp256k1-dev \
sqlite-dev \
sqlite-static
# Alpine provides libsecp256k1 shared objects, but we need a static archive
# for fully-static musl linking. Build and install libsecp256k1.a in /usr/local.
RUN git clone --depth 1 https://github.com/bitcoin-core/secp256k1.git /tmp/secp256k1 && \
cd /tmp/secp256k1 && \
./autogen.sh && \
./configure --enable-static --disable-shared --prefix=/usr/local && \
make -j"$(nproc)" && \
make install && \
rm -rf /tmp/secp256k1
WORKDIR /src

View File

@@ -0,0 +1,32 @@
# Shared Alpine/musl static builder pattern
This directory defines the common static build base used by `stack/*` C99 binaries.
## Why this exists
Per [F30](../../plans/threat_model.md#46-image-build-and-reproducibility), every n-OS-tr C binary must be built in Alpine and statically linked against musl so `ldd` reports `not a dynamic executable`.
## Files
- `Dockerfile.alpine-musl`: shared Alpine 3.19 base with common musl/static build dependencies.
- `build_static.sh`: generic wrapper that:
- accepts `<target-dir>`
- picks `linux/amd64` or `linux/arm64` using `uname -m` (or `ARCH=arm64` override)
- builds the shared base image and the target app image
- extracts the built binary to `<target-dir>/build/<basename>_static_<arch>`
## Invocation pattern from subtrees
Each stack component keeps its own `Dockerfile.alpine-musl` and a thin wrapper script:
```bash
./stack/<component>/build_static.sh
```
The component wrapper calls this shared script as:
```bash
../build-common/build_static.sh .
```
from inside that component directory.

View File

@@ -0,0 +1,110 @@
#!/bin/bash
set -euo pipefail
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <target-dir>"
exit 1
fi
TARGET_DIR="$1"
TARGET_ABS="$(cd "$TARGET_DIR" && pwd)"
COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASENAME="$(basename "$TARGET_ABS")"
BUILD_DIR="$TARGET_ABS/build"
DOCKERFILE="$TARGET_ABS/Dockerfile.alpine-musl"
if [ ! -f "$DOCKERFILE" ]; then
echo "ERROR: Missing Dockerfile: $DOCKERFILE"
exit 1
fi
if ! command -v docker >/dev/null 2>&1; then
echo "ERROR: docker is not installed or not in PATH"
exit 1
fi
if ! docker info >/dev/null 2>&1; then
echo "ERROR: docker daemon is not reachable"
exit 1
fi
HOST_ARCH="$(uname -m)"
REQUESTED_ARCH="${ARCH:-}"
if [ -n "$REQUESTED_ARCH" ]; then
case "$REQUESTED_ARCH" in
arm64|aarch64)
PLATFORM="linux/arm64"
OUTPUT_ARCH="arm64"
;;
amd64|x86_64)
PLATFORM="linux/amd64"
OUTPUT_ARCH="x86_64"
;;
*)
echo "ERROR: Unsupported ARCH='$REQUESTED_ARCH' (supported: arm64, amd64)"
exit 1
;;
esac
else
case "$HOST_ARCH" in
x86_64)
PLATFORM="linux/amd64"
OUTPUT_ARCH="x86_64"
;;
aarch64|arm64)
PLATFORM="linux/arm64"
OUTPUT_ARCH="arm64"
;;
*)
echo "WARNING: Unknown host architecture '$HOST_ARCH', defaulting to linux/amd64"
PLATFORM="linux/amd64"
OUTPUT_ARCH="$HOST_ARCH"
;;
esac
fi
OUTPUT_NAME="${BASENAME}_static_${OUTPUT_ARCH}"
BASE_IMAGE_TAG="n-os-tr/alpine-musl-base:3.19"
APP_IMAGE_TAG="n-os-tr/${BASENAME}-musl-builder:latest"
mkdir -p "$BUILD_DIR"
echo "=========================================="
echo "n-OS-tr Alpine/musl static build"
echo "=========================================="
echo "Target: $TARGET_ABS"
echo "Platform: $PLATFORM"
echo "Output: $BUILD_DIR/$OUTPUT_NAME"
echo ""
echo "[1/3] Building shared base image ($BASE_IMAGE_TAG)"
docker build \
--platform "$PLATFORM" \
-f "$COMMON_DIR/Dockerfile.alpine-musl" \
-t "$BASE_IMAGE_TAG" \
"$COMMON_DIR"
echo "[2/3] Building application image ($APP_IMAGE_TAG)"
docker build \
--platform "$PLATFORM" \
-f "$DOCKERFILE" \
-t "$APP_IMAGE_TAG" \
"$TARGET_ABS"
echo "[3/3] Extracting binary"
CONTAINER_ID="$(docker create "$APP_IMAGE_TAG" "/$BASENAME")"
trap 'docker rm -f "$CONTAINER_ID" >/dev/null 2>&1 || true' EXIT
docker cp "$CONTAINER_ID:/$BASENAME" "$BUILD_DIR/$OUTPUT_NAME"
chmod +x "$BUILD_DIR/$OUTPUT_NAME"
FILE_INFO="$(file "$BUILD_DIR/$OUTPUT_NAME")"
echo "$FILE_INFO"
if LDD_OUT="$(ldd "$BUILD_DIR/$OUTPUT_NAME" 2>&1 || true)"; then
echo "$LDD_OUT"
fi
echo "Built: $BUILD_DIR/$OUTPUT_NAME"
echo "Size: $(stat -c '%s bytes' "$BUILD_DIR/$OUTPUT_NAME")"

1
stack/nostr-id-tui/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build/

View File

@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.16)
project(nostr-id-tui C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
find_package(Curses REQUIRED)
option(NOSTR_TUI_FORCE_STATIC "Link nostr-id-tui as fully static binary" OFF)
set(NOSTR_CORE_LIB_DIR "${CMAKE_SOURCE_DIR}/../../includes/nostr_core_lib" CACHE PATH "Path to nostr_core_lib directory")
set(NOSTR_CORE_STATIC_LIB "${NOSTR_CORE_LIB_DIR}/libnostr_core_x64.a" CACHE FILEPATH "Path to libnostr_core static archive")
if(NOT EXISTS "${NOSTR_CORE_STATIC_LIB}")
message(FATAL_ERROR "Missing ${NOSTR_CORE_STATIC_LIB}. Build it first with: cd includes/nostr_core_lib && ./build.sh x64")
endif()
add_executable(nostr-id-tui
src/main.c
)
target_include_directories(nostr-id-tui PRIVATE
include
${NOSTR_CORE_LIB_DIR}
${NOSTR_CORE_LIB_DIR}/nostr_core
)
target_compile_options(nostr-id-tui PRIVATE -Wall -Wextra -Wpedantic)
target_link_libraries(nostr-id-tui PRIVATE
${NOSTR_CORE_STATIC_LIB}
${CURSES_LIBRARIES}
secp256k1
ssl
crypto
curl
z
pthread
m
dl
)
if(NOSTR_TUI_FORCE_STATIC)
target_link_options(nostr-id-tui PRIVATE -static)
endif()

View File

@@ -0,0 +1,26 @@
ARG BASE_IMAGE=n-os-tr/alpine-musl-base:3.19
FROM ${BASE_IMAGE} AS builder
RUN apk add --no-cache \
bash \
ncurses-dev \
ncurses-static
WORKDIR /work
COPY includes/nostr_core_lib /work/includes/nostr_core_lib
COPY stack/nostr-id-tui /work/stack/nostr-id-tui
RUN cd /work/includes/nostr_core_lib && bash ./build.sh x64
RUN cmake -S /work/stack/nostr-id-tui -B /work/stack/nostr-id-tui/build-cmake \
-DCMAKE_BUILD_TYPE=Release \
-DNOSTR_CORE_LIB_DIR=/work/includes/nostr_core_lib \
-DNOSTR_CORE_STATIC_LIB=/work/includes/nostr_core_lib/libnostr_core_x64.a \
-DNOSTR_TUI_FORCE_STATIC=ON && \
cmake --build /work/stack/nostr-id-tui/build-cmake --target nostr-id-tui -j"$(nproc)" && \
mkdir -p /out && \
cp /work/stack/nostr-id-tui/build-cmake/nostr-id-tui /out/nostr-id-tui
FROM scratch AS output
COPY --from=builder /out/nostr-id-tui /nostr-id-tui

View File

@@ -0,0 +1,53 @@
# nostr-id-tui
Boot-time terminal identity UI for n-OS-tr.
Current local implementation provides a functional ncurses flow:
- Main menu
- Enter existing mnemonic -> validate -> derive account 0 `npub`
- Generate new mnemonic -> derive account 0 `npub` -> show random confirmation positions
## Local host dev loop (fast iteration)
Use the wrapper script to auto-bootstrap `nostr_core_lib` (if missing), rebuild, and run in your current terminal:
```bash
./stack/nostr-id-tui/dev.sh
```
What it does:
1. If `includes/nostr_core_lib/libnostr_core_x64.a` is missing, builds it with `./build.sh x64`.
2. Configures `stack/nostr-id-tui/build-host` with CMake.
3. Rebuilds `nostr-id-tui` for host.
4. Runs it with `TERM=${TERM:-xterm-256color}`.
Manual equivalent (if preferred):
```bash
cmake -S stack/nostr-id-tui -B stack/nostr-id-tui/build-host
cmake --build stack/nostr-id-tui/build-host -j
TERM=xterm-256color ./stack/nostr-id-tui/build-host/nostr-id-tui
```
## Static Docker build (musl, reproducible)
This path now builds `nostr_core_lib` inside Alpine and links `nostr-id-tui` fully static against musl.
```bash
./stack/nostr-id-tui/build_static.sh
```
Output:
- `stack/nostr-id-tui/build/nostr-id-tui_static_x86_64`
## Notes
- Uses upstream helpers from `nostr_core_lib`:
- `nostr_secure_memory`
- `nostr_bip39_ui`
- `nostr_generate_mnemonic_and_keys`
- `nostr_derive_keys_from_mnemonic`
- Identity-agent IPC and systemd boot wiring are intentionally out of scope for this step.

View File

@@ -0,0 +1,428 @@
# This is the CMakeCache file.
# For build in directory: /home/user/lt/n_os_tr/stack/nostr-id-tui/build-host
# It was generated by CMake: /usr/bin/cmake
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.
########################
# EXTERNAL cache entries
########################
//Path to a program.
CMAKE_ADDR2LINE:FILEPATH=/usr/bin/addr2line
//Path to a program.
CMAKE_AR:FILEPATH=/usr/bin/ar
//Choose the type of build, options are: None Debug Release RelWithDebInfo
// MinSizeRel ...
CMAKE_BUILD_TYPE:STRING=Debug
//Enable/Disable color output during build.
CMAKE_COLOR_MAKEFILE:BOOL=ON
//C compiler
CMAKE_C_COMPILER:FILEPATH=/usr/bin/cc
//A wrapper around 'ar' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_C_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-14
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_C_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-14
//Flags used by the C compiler during all build types.
CMAKE_C_FLAGS:STRING=
//Flags used by the C compiler during DEBUG builds.
CMAKE_C_FLAGS_DEBUG:STRING=-g
//Flags used by the C compiler during MINSIZEREL builds.
CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
//Flags used by the C compiler during RELEASE builds.
CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
//Flags used by the C compiler during RELWITHDEBINFO builds.
CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//Path to a program.
CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND
//Flags used by the linker during all build types.
CMAKE_EXE_LINKER_FLAGS:STRING=
//Flags used by the linker during DEBUG builds.
CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during MINSIZEREL builds.
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during RELEASE builds.
CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during RELWITHDEBINFO builds.
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Enable/Disable output of compile commands during generation.
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=
//Value Computed by CMake.
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/pkgRedirects
//Install path prefix, prepended onto install directories.
CMAKE_INSTALL_PREFIX:PATH=/usr/local
//Path to a program.
CMAKE_LINKER:FILEPATH=/usr/bin/ld
//Path to a program.
CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/gmake
//Flags used by the linker during the creation of modules during
// all build types.
CMAKE_MODULE_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of modules during
// DEBUG builds.
CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of modules during
// MINSIZEREL builds.
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of modules during
// RELEASE builds.
CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of modules during
// RELWITHDEBINFO builds.
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Path to a program.
CMAKE_NM:FILEPATH=/usr/bin/nm
//Path to a program.
CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy
//Path to a program.
CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump
//Value Computed by CMake
CMAKE_PROJECT_DESCRIPTION:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_NAME:STATIC=nostr-id-tui
//Path to a program.
CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib
//Path to a program.
CMAKE_READELF:FILEPATH=/usr/bin/readelf
//Flags used by the linker during the creation of shared libraries
// during all build types.
CMAKE_SHARED_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of shared libraries
// during DEBUG builds.
CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of shared libraries
// during MINSIZEREL builds.
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of shared libraries
// during RELEASE builds.
CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of shared libraries
// during RELWITHDEBINFO builds.
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//If set, runtime paths are not added when installing shared libraries,
// but are added when building.
CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
//If set, runtime paths are not added when using shared libraries.
CMAKE_SKIP_RPATH:BOOL=NO
//Flags used by the linker during the creation of static libraries
// during all build types.
CMAKE_STATIC_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of static libraries
// during DEBUG builds.
CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of static libraries
// during MINSIZEREL builds.
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of static libraries
// during RELEASE builds.
CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of static libraries
// during RELWITHDEBINFO builds.
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Path to a program.
CMAKE_STRIP:FILEPATH=/usr/bin/strip
//Path to a program.
CMAKE_TAPI:FILEPATH=CMAKE_TAPI-NOTFOUND
//If this value is on, makefiles will be generated without the
// .SILENT directive, and all commands will be echoed to the console
// during the make. This is useful for debugging only. With Visual
// Studio IDE projects all commands are done without /nologo.
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
//Path to a library.
CURSES_CURSES_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libcurses.so
//Path to a library.
CURSES_FORM_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libform.so
//Path to a file.
CURSES_INCLUDE_PATH:PATH=/usr/include
//Path to a library.
CURSES_NCURSES_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libncurses.so
//Path to nostr_core_lib directory
NOSTR_CORE_LIB_DIR:PATH=/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib
//Path to libnostr_core static archive
NOSTR_CORE_STATIC_LIB:FILEPATH=/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/libnostr_core_x64.a
//Link nostr-id-tui as fully static binary
NOSTR_TUI_FORCE_STATIC:BOOL=OFF
//Arguments to supply to pkg-config
PKG_CONFIG_ARGN:STRING=
//pkg-config executable
PKG_CONFIG_EXECUTABLE:FILEPATH=/usr/bin/pkg-config
//Value Computed by CMake
nostr-id-tui_BINARY_DIR:STATIC=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host
//Value Computed by CMake
nostr-id-tui_IS_TOP_LEVEL:STATIC=ON
//Value Computed by CMake
nostr-id-tui_SOURCE_DIR:STATIC=/home/user/lt/n_os_tr/stack/nostr-id-tui
//Path to a library.
pkgcfg_lib_NCURSES_ncurses:FILEPATH=/usr/lib/x86_64-linux-gnu/libncurses.so
//Path to a library.
pkgcfg_lib_NCURSES_tinfo:FILEPATH=/usr/lib/x86_64-linux-gnu/libtinfo.so
########################
# INTERNAL cache entries
########################
//ADVANCED property for variable: CMAKE_ADDR2LINE
CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_AR
CMAKE_AR-ADVANCED:INTERNAL=1
//This is the directory where this CMakeCache.txt was created
CMAKE_CACHEFILE_DIR:INTERNAL=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host
//Major version of cmake used to create the current loaded cache
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
//Minor version of cmake used to create the current loaded cache
CMAKE_CACHE_MINOR_VERSION:INTERNAL=31
//Patch version of cmake used to create the current loaded cache
CMAKE_CACHE_PATCH_VERSION:INTERNAL=6
//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE
CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1
//Path to CMake executable.
CMAKE_COMMAND:INTERNAL=/usr/bin/cmake
//Path to cpack program executable.
CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack
//Path to ctest program executable.
CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest
//ADVANCED property for variable: CMAKE_C_COMPILER
CMAKE_C_COMPILER-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER_AR
CMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB
CMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS
CMAKE_C_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_DLLTOOL
CMAKE_DLLTOOL-ADVANCED:INTERNAL=1
//Executable file format
CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS
CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1
//Name of external makefile project generator.
CMAKE_EXTRA_GENERATOR:INTERNAL=
//Name of generator.
CMAKE_GENERATOR:INTERNAL=Unix Makefiles
//Generator instance identifier.
CMAKE_GENERATOR_INSTANCE:INTERNAL=
//Name of generator platform.
CMAKE_GENERATOR_PLATFORM:INTERNAL=
//Name of generator toolset.
CMAKE_GENERATOR_TOOLSET:INTERNAL=
//Source directory with the top level CMakeLists.txt file for this
// project
CMAKE_HOME_DIRECTORY:INTERNAL=/home/user/lt/n_os_tr/stack/nostr-id-tui
//Install .so files without execute permission.
CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1
//ADVANCED property for variable: CMAKE_LINKER
CMAKE_LINKER-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_NM
CMAKE_NM-ADVANCED:INTERNAL=1
//number of local generators
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJCOPY
CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJDUMP
CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
//Platform information initialized
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
//ADVANCED property for variable: CMAKE_RANLIB
CMAKE_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_READELF
CMAKE_READELF-ADVANCED:INTERNAL=1
//Path to CMake installation.
CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.31
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_RPATH
CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STRIP
CMAKE_STRIP-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_TAPI
CMAKE_TAPI-ADVANCED:INTERNAL=1
//uname command
CMAKE_UNAME:INTERNAL=/usr/bin/uname
//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CURSES_CURSES_LIBRARY
CURSES_CURSES_LIBRARY-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CURSES_FORM_LIBRARY
CURSES_FORM_LIBRARY-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CURSES_INCLUDE_PATH
CURSES_INCLUDE_PATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CURSES_NCURSES_LIBRARY
CURSES_NCURSES_LIBRARY-ADVANCED:INTERNAL=1
//Details about finding Curses
FIND_PACKAGE_MESSAGE_DETAILS_Curses:INTERNAL=[/usr/lib/x86_64-linux-gnu/libcurses.so][/usr/include][v()]
NCURSES_CFLAGS:INTERNAL=-D_DEFAULT_SOURCE;-D_XOPEN_SOURCE=600
NCURSES_CFLAGS_I:INTERNAL=
NCURSES_CFLAGS_OTHER:INTERNAL=-D_DEFAULT_SOURCE;-D_XOPEN_SOURCE=600
NCURSES_FOUND:INTERNAL=1
NCURSES_INCLUDEDIR:INTERNAL=/usr/include
NCURSES_INCLUDE_DIRS:INTERNAL=
NCURSES_LDFLAGS:INTERNAL=-lncurses;-ltinfo
NCURSES_LDFLAGS_OTHER:INTERNAL=
NCURSES_LIBDIR:INTERNAL=/usr/lib/x86_64-linux-gnu
NCURSES_LIBRARIES:INTERNAL=ncurses;tinfo
NCURSES_LIBRARY_DIRS:INTERNAL=
NCURSES_LIBS:INTERNAL=
NCURSES_LIBS_L:INTERNAL=
NCURSES_LIBS_OTHER:INTERNAL=
NCURSES_LIBS_PATHS:INTERNAL=
NCURSES_MODULE_NAME:INTERNAL=ncurses
NCURSES_PREFIX:INTERNAL=/usr
NCURSES_STATIC_CFLAGS:INTERNAL=-D_DEFAULT_SOURCE;-D_XOPEN_SOURCE=600
NCURSES_STATIC_CFLAGS_I:INTERNAL=
NCURSES_STATIC_CFLAGS_OTHER:INTERNAL=-D_DEFAULT_SOURCE;-D_XOPEN_SOURCE=600
NCURSES_STATIC_INCLUDE_DIRS:INTERNAL=
NCURSES_STATIC_LDFLAGS:INTERNAL=-lncurses;-ltinfo;-ldl
NCURSES_STATIC_LDFLAGS_OTHER:INTERNAL=
NCURSES_STATIC_LIBDIR:INTERNAL=
NCURSES_STATIC_LIBRARIES:INTERNAL=ncurses;tinfo;dl
NCURSES_STATIC_LIBRARY_DIRS:INTERNAL=
NCURSES_STATIC_LIBS:INTERNAL=
NCURSES_STATIC_LIBS_L:INTERNAL=
NCURSES_STATIC_LIBS_OTHER:INTERNAL=
NCURSES_STATIC_LIBS_PATHS:INTERNAL=
NCURSES_VERSION:INTERNAL=6.5.20250216
NCURSES_ncurses_INCLUDEDIR:INTERNAL=
NCURSES_ncurses_LIBDIR:INTERNAL=
NCURSES_ncurses_PREFIX:INTERNAL=
NCURSES_ncurses_VERSION:INTERNAL=
//ADVANCED property for variable: PKG_CONFIG_ARGN
PKG_CONFIG_ARGN-ADVANCED:INTERNAL=1
//ADVANCED property for variable: PKG_CONFIG_EXECUTABLE
PKG_CONFIG_EXECUTABLE-ADVANCED:INTERNAL=1
//linker supports push/pop state
_CMAKE_C_LINKER_PUSHPOP_STATE_SUPPORTED:INTERNAL=TRUE
//linker supports push/pop state
_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED:INTERNAL=TRUE
__pkg_config_arguments_NCURSES:INTERNAL=QUIET;ncurses
__pkg_config_checked_NCURSES:INTERNAL=1
//ADVANCED property for variable: pkgcfg_lib_NCURSES_ncurses
pkgcfg_lib_NCURSES_ncurses-ADVANCED:INTERNAL=1
//ADVANCED property for variable: pkgcfg_lib_NCURSES_tinfo
pkgcfg_lib_NCURSES_tinfo-ADVANCED:INTERNAL=1
prefix_result:INTERNAL=/usr/lib/x86_64-linux-gnu

View File

@@ -0,0 +1,81 @@
set(CMAKE_C_COMPILER "/usr/bin/cc")
set(CMAKE_C_COMPILER_ARG1 "")
set(CMAKE_C_COMPILER_ID "GNU")
set(CMAKE_C_COMPILER_VERSION "14.2.0")
set(CMAKE_C_COMPILER_VERSION_INTERNAL "")
set(CMAKE_C_COMPILER_WRAPPER "")
set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "17")
set(CMAKE_C_EXTENSIONS_COMPUTED_DEFAULT "ON")
set(CMAKE_C_STANDARD_LATEST "23")
set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert;c_std_17;c_std_23")
set(CMAKE_C90_COMPILE_FEATURES "c_std_90;c_function_prototypes")
set(CMAKE_C99_COMPILE_FEATURES "c_std_99;c_restrict;c_variadic_macros")
set(CMAKE_C11_COMPILE_FEATURES "c_std_11;c_static_assert")
set(CMAKE_C17_COMPILE_FEATURES "c_std_17")
set(CMAKE_C23_COMPILE_FEATURES "c_std_23")
set(CMAKE_C_PLATFORM_ID "Linux")
set(CMAKE_C_SIMULATE_ID "")
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "GNU")
set(CMAKE_C_SIMULATE_VERSION "")
set(CMAKE_AR "/usr/bin/ar")
set(CMAKE_C_COMPILER_AR "/usr/bin/gcc-ar-14")
set(CMAKE_RANLIB "/usr/bin/ranlib")
set(CMAKE_C_COMPILER_RANLIB "/usr/bin/gcc-ranlib-14")
set(CMAKE_LINKER "/usr/bin/ld")
set(CMAKE_LINKER_LINK "")
set(CMAKE_LINKER_LLD "")
set(CMAKE_C_COMPILER_LINKER "/usr/bin/ld")
set(CMAKE_C_COMPILER_LINKER_ID "GNU")
set(CMAKE_C_COMPILER_LINKER_VERSION 2.44)
set(CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT GNU)
set(CMAKE_MT "")
set(CMAKE_TAPI "CMAKE_TAPI-NOTFOUND")
set(CMAKE_COMPILER_IS_GNUCC 1)
set(CMAKE_C_COMPILER_LOADED 1)
set(CMAKE_C_COMPILER_WORKS TRUE)
set(CMAKE_C_ABI_COMPILED TRUE)
set(CMAKE_C_COMPILER_ENV_VAR "CC")
set(CMAKE_C_COMPILER_ID_RUN 1)
set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m)
set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
set(CMAKE_C_LINKER_PREFERENCE 10)
set(CMAKE_C_LINKER_DEPFILE_SUPPORTED )
# Save compiler ABI information.
set(CMAKE_C_SIZEOF_DATA_PTR "8")
set(CMAKE_C_COMPILER_ABI "ELF")
set(CMAKE_C_BYTE_ORDER "LITTLE_ENDIAN")
set(CMAKE_C_LIBRARY_ARCHITECTURE "x86_64-linux-gnu")
if(CMAKE_C_SIZEOF_DATA_PTR)
set(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}")
endif()
if(CMAKE_C_COMPILER_ABI)
set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}")
endif()
if(CMAKE_C_LIBRARY_ARCHITECTURE)
set(CMAKE_LIBRARY_ARCHITECTURE "x86_64-linux-gnu")
endif()
set(CMAKE_C_CL_SHOWINCLUDES_PREFIX "")
if(CMAKE_C_CL_SHOWINCLUDES_PREFIX)
set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_C_CL_SHOWINCLUDES_PREFIX}")
endif()
set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/14/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include")
set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "gcc;gcc_s;c;gcc;gcc_s")
set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/14;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib")
set(CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "")

View File

@@ -0,0 +1,15 @@
set(CMAKE_HOST_SYSTEM "Linux-6.12.59-1.qubes.fc41.x86_64")
set(CMAKE_HOST_SYSTEM_NAME "Linux")
set(CMAKE_HOST_SYSTEM_VERSION "6.12.59-1.qubes.fc41.x86_64")
set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64")
set(CMAKE_SYSTEM "Linux-6.12.59-1.qubes.fc41.x86_64")
set(CMAKE_SYSTEM_NAME "Linux")
set(CMAKE_SYSTEM_VERSION "6.12.59-1.qubes.fc41.x86_64")
set(CMAKE_SYSTEM_PROCESSOR "x86_64")
set(CMAKE_CROSSCOMPILING "FALSE")
set(CMAKE_SYSTEM_LOADED 1)

View File

@@ -0,0 +1,904 @@
#ifdef __cplusplus
# error "A C++ compiler has been selected for C."
#endif
#if defined(__18CXX)
# define ID_VOID_MAIN
#endif
#if defined(__CLASSIC_C__)
/* cv-qualifiers did not exist in K&R C */
# define const
# define volatile
#endif
#if !defined(__has_include)
/* If the compiler does not have __has_include, pretend the answer is
always no. */
# define __has_include(x) 0
#endif
/* Version number components: V=Version, R=Revision, P=Patch
Version date components: YYYY=Year, MM=Month, DD=Day */
#if defined(__INTEL_COMPILER) || defined(__ICC)
# define COMPILER_ID "Intel"
# if defined(_MSC_VER)
# define SIMULATE_ID "MSVC"
# endif
# if defined(__GNUC__)
# define SIMULATE_ID "GNU"
# endif
/* __INTEL_COMPILER = VRP prior to 2021, and then VVVV for 2021 and later,
except that a few beta releases use the old format with V=2021. */
# if __INTEL_COMPILER < 2021 || __INTEL_COMPILER == 202110 || __INTEL_COMPILER == 202111
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100)
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10)
# if defined(__INTEL_COMPILER_UPDATE)
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE)
# else
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10)
# endif
# else
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER)
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER_UPDATE)
/* The third version component from --version is an update index,
but no macro is provided for it. */
# define COMPILER_VERSION_PATCH DEC(0)
# endif
# if defined(__INTEL_COMPILER_BUILD_DATE)
/* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */
# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE)
# endif
# if defined(_MSC_VER)
/* _MSC_VER = VVRR */
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
# endif
# if defined(__GNUC__)
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
# elif defined(__GNUG__)
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
# endif
# if defined(__GNUC_MINOR__)
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
# endif
# if defined(__GNUC_PATCHLEVEL__)
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
# endif
#elif (defined(__clang__) && defined(__INTEL_CLANG_COMPILER)) || defined(__INTEL_LLVM_COMPILER)
# define COMPILER_ID "IntelLLVM"
#if defined(_MSC_VER)
# define SIMULATE_ID "MSVC"
#endif
#if defined(__GNUC__)
# define SIMULATE_ID "GNU"
#endif
/* __INTEL_LLVM_COMPILER = VVVVRP prior to 2021.2.0, VVVVRRPP for 2021.2.0 and
* later. Look for 6 digit vs. 8 digit version number to decide encoding.
* VVVV is no smaller than the current year when a version is released.
*/
#if __INTEL_LLVM_COMPILER < 1000000L
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/100)
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/10 % 10)
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 10)
#else
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/10000)
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/100 % 100)
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 100)
#endif
#if defined(_MSC_VER)
/* _MSC_VER = VVRR */
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
#endif
#if defined(__GNUC__)
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
#elif defined(__GNUG__)
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
#endif
#if defined(__GNUC_MINOR__)
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
#endif
#if defined(__GNUC_PATCHLEVEL__)
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
#endif
#elif defined(__PATHCC__)
# define COMPILER_ID "PathScale"
# define COMPILER_VERSION_MAJOR DEC(__PATHCC__)
# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__)
# if defined(__PATHCC_PATCHLEVEL__)
# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__)
# endif
#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__)
# define COMPILER_ID "Embarcadero"
# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF)
# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF)
# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF)
#elif defined(__BORLANDC__)
# define COMPILER_ID "Borland"
/* __BORLANDC__ = 0xVRR */
# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8)
# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF)
#elif defined(__WATCOMC__) && __WATCOMC__ < 1200
# define COMPILER_ID "Watcom"
/* __WATCOMC__ = VVRR */
# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100)
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
# if (__WATCOMC__ % 10) > 0
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
# endif
#elif defined(__WATCOMC__)
# define COMPILER_ID "OpenWatcom"
/* __WATCOMC__ = VVRP + 1100 */
# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100)
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
# if (__WATCOMC__ % 10) > 0
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
# endif
#elif defined(__SUNPRO_C)
# define COMPILER_ID "SunPro"
# if __SUNPRO_C >= 0x5100
/* __SUNPRO_C = 0xVRRP */
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>12)
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xFF)
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF)
# else
/* __SUNPRO_CC = 0xVRP */
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>8)
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xF)
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF)
# endif
#elif defined(__HP_cc)
# define COMPILER_ID "HP"
/* __HP_cc = VVRRPP */
# define COMPILER_VERSION_MAJOR DEC(__HP_cc/10000)
# define COMPILER_VERSION_MINOR DEC(__HP_cc/100 % 100)
# define COMPILER_VERSION_PATCH DEC(__HP_cc % 100)
#elif defined(__DECC)
# define COMPILER_ID "Compaq"
/* __DECC_VER = VVRRTPPPP */
# define COMPILER_VERSION_MAJOR DEC(__DECC_VER/10000000)
# define COMPILER_VERSION_MINOR DEC(__DECC_VER/100000 % 100)
# define COMPILER_VERSION_PATCH DEC(__DECC_VER % 10000)
#elif defined(__IBMC__) && defined(__COMPILER_VER__)
# define COMPILER_ID "zOS"
/* __IBMC__ = VRP */
# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
#elif defined(__open_xl__) && defined(__clang__)
# define COMPILER_ID "IBMClang"
# define COMPILER_VERSION_MAJOR DEC(__open_xl_version__)
# define COMPILER_VERSION_MINOR DEC(__open_xl_release__)
# define COMPILER_VERSION_PATCH DEC(__open_xl_modification__)
# define COMPILER_VERSION_TWEAK DEC(__open_xl_ptf_fix_level__)
#elif defined(__ibmxl__) && defined(__clang__)
# define COMPILER_ID "XLClang"
# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__)
# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__)
# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__)
# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__)
#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ >= 800
# define COMPILER_ID "XL"
/* __IBMC__ = VRP */
# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ < 800
# define COMPILER_ID "VisualAge"
/* __IBMC__ = VRP */
# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
#elif defined(__NVCOMPILER)
# define COMPILER_ID "NVHPC"
# define COMPILER_VERSION_MAJOR DEC(__NVCOMPILER_MAJOR__)
# define COMPILER_VERSION_MINOR DEC(__NVCOMPILER_MINOR__)
# if defined(__NVCOMPILER_PATCHLEVEL__)
# define COMPILER_VERSION_PATCH DEC(__NVCOMPILER_PATCHLEVEL__)
# endif
#elif defined(__PGI)
# define COMPILER_ID "PGI"
# define COMPILER_VERSION_MAJOR DEC(__PGIC__)
# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__)
# if defined(__PGIC_PATCHLEVEL__)
# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__)
# endif
#elif defined(__clang__) && defined(__cray__)
# define COMPILER_ID "CrayClang"
# define COMPILER_VERSION_MAJOR DEC(__cray_major__)
# define COMPILER_VERSION_MINOR DEC(__cray_minor__)
# define COMPILER_VERSION_PATCH DEC(__cray_patchlevel__)
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
#elif defined(_CRAYC)
# define COMPILER_ID "Cray"
# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR)
# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR)
#elif defined(__TI_COMPILER_VERSION__)
# define COMPILER_ID "TI"
/* __TI_COMPILER_VERSION__ = VVVRRRPPP */
# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000)
# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000)
# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000)
#elif defined(__CLANG_FUJITSU)
# define COMPILER_ID "FujitsuClang"
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
#elif defined(__FUJITSU)
# define COMPILER_ID "Fujitsu"
# if defined(__FCC_version__)
# define COMPILER_VERSION __FCC_version__
# elif defined(__FCC_major__)
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
# endif
# if defined(__fcc_version)
# define COMPILER_VERSION_INTERNAL DEC(__fcc_version)
# elif defined(__FCC_VERSION)
# define COMPILER_VERSION_INTERNAL DEC(__FCC_VERSION)
# endif
#elif defined(__ghs__)
# define COMPILER_ID "GHS"
/* __GHS_VERSION_NUMBER = VVVVRP */
# ifdef __GHS_VERSION_NUMBER
# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100)
# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10)
# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10)
# endif
#elif defined(__TASKING__)
# define COMPILER_ID "Tasking"
# define COMPILER_VERSION_MAJOR DEC(__VERSION__/1000)
# define COMPILER_VERSION_MINOR DEC(__VERSION__ % 100)
# define COMPILER_VERSION_INTERNAL DEC(__VERSION__)
#elif defined(__ORANGEC__)
# define COMPILER_ID "OrangeC"
# define COMPILER_VERSION_MAJOR DEC(__ORANGEC_MAJOR__)
# define COMPILER_VERSION_MINOR DEC(__ORANGEC_MINOR__)
# define COMPILER_VERSION_PATCH DEC(__ORANGEC_PATCHLEVEL__)
#elif defined(__TINYC__)
# define COMPILER_ID "TinyCC"
#elif defined(__BCC__)
# define COMPILER_ID "Bruce"
#elif defined(__SCO_VERSION__)
# define COMPILER_ID "SCO"
#elif defined(__ARMCC_VERSION) && !defined(__clang__)
# define COMPILER_ID "ARMCC"
#if __ARMCC_VERSION >= 1000000
/* __ARMCC_VERSION = VRRPPPP */
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000)
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100)
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
#else
/* __ARMCC_VERSION = VRPPPP */
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000)
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10)
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
#endif
#elif defined(__clang__) && defined(__apple_build_version__)
# define COMPILER_ID "AppleClang"
# if defined(_MSC_VER)
# define SIMULATE_ID "MSVC"
# endif
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
# if defined(_MSC_VER)
/* _MSC_VER = VVRR */
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
# endif
# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__)
#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION)
# define COMPILER_ID "ARMClang"
# define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000)
# define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100)
# define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION/100 % 100)
# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION)
#elif defined(__clang__) && defined(__ti__)
# define COMPILER_ID "TIClang"
# define COMPILER_VERSION_MAJOR DEC(__ti_major__)
# define COMPILER_VERSION_MINOR DEC(__ti_minor__)
# define COMPILER_VERSION_PATCH DEC(__ti_patchlevel__)
# define COMPILER_VERSION_INTERNAL DEC(__ti_version__)
#elif defined(__clang__)
# define COMPILER_ID "Clang"
# if defined(_MSC_VER)
# define SIMULATE_ID "MSVC"
# endif
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
# if defined(_MSC_VER)
/* _MSC_VER = VVRR */
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
# endif
#elif defined(__LCC__) && (defined(__GNUC__) || defined(__GNUG__) || defined(__MCST__))
# define COMPILER_ID "LCC"
# define COMPILER_VERSION_MAJOR DEC(__LCC__ / 100)
# define COMPILER_VERSION_MINOR DEC(__LCC__ % 100)
# if defined(__LCC_MINOR__)
# define COMPILER_VERSION_PATCH DEC(__LCC_MINOR__)
# endif
# if defined(__GNUC__) && defined(__GNUC_MINOR__)
# define SIMULATE_ID "GNU"
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
# if defined(__GNUC_PATCHLEVEL__)
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
# endif
# endif
#elif defined(__GNUC__)
# define COMPILER_ID "GNU"
# define COMPILER_VERSION_MAJOR DEC(__GNUC__)
# if defined(__GNUC_MINOR__)
# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__)
# endif
# if defined(__GNUC_PATCHLEVEL__)
# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
# endif
#elif defined(_MSC_VER)
# define COMPILER_ID "MSVC"
/* _MSC_VER = VVRR */
# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100)
# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100)
# if defined(_MSC_FULL_VER)
# if _MSC_VER >= 1400
/* _MSC_FULL_VER = VVRRPPPPP */
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000)
# else
/* _MSC_FULL_VER = VVRRPPPP */
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000)
# endif
# endif
# if defined(_MSC_BUILD)
# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD)
# endif
#elif defined(_ADI_COMPILER)
# define COMPILER_ID "ADSP"
#if defined(__VERSIONNUM__)
/* __VERSIONNUM__ = 0xVVRRPPTT */
# define COMPILER_VERSION_MAJOR DEC(__VERSIONNUM__ >> 24 & 0xFF)
# define COMPILER_VERSION_MINOR DEC(__VERSIONNUM__ >> 16 & 0xFF)
# define COMPILER_VERSION_PATCH DEC(__VERSIONNUM__ >> 8 & 0xFF)
# define COMPILER_VERSION_TWEAK DEC(__VERSIONNUM__ & 0xFF)
#endif
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
# define COMPILER_ID "IAR"
# if defined(__VER__) && defined(__ICCARM__)
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000)
# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000)
# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000)
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__) || defined(__ICCSTM8__))
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100)
# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100))
# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__)
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
# endif
#elif defined(__SDCC_VERSION_MAJOR) || defined(SDCC)
# define COMPILER_ID "SDCC"
# if defined(__SDCC_VERSION_MAJOR)
# define COMPILER_VERSION_MAJOR DEC(__SDCC_VERSION_MAJOR)
# define COMPILER_VERSION_MINOR DEC(__SDCC_VERSION_MINOR)
# define COMPILER_VERSION_PATCH DEC(__SDCC_VERSION_PATCH)
# else
/* SDCC = VRP */
# define COMPILER_VERSION_MAJOR DEC(SDCC/100)
# define COMPILER_VERSION_MINOR DEC(SDCC/10 % 10)
# define COMPILER_VERSION_PATCH DEC(SDCC % 10)
# endif
/* These compilers are either not known or too old to define an
identification macro. Try to identify the platform and guess that
it is the native compiler. */
#elif defined(__hpux) || defined(__hpua)
# define COMPILER_ID "HP"
#else /* unknown compiler */
# define COMPILER_ID ""
#endif
/* Construct the string literal in pieces to prevent the source from
getting matched. Store it in a pointer rather than an array
because some compilers will just produce instructions to fill the
array rather than assigning a pointer to a static array. */
char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
#ifdef SIMULATE_ID
char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
#endif
#ifdef __QNXNTO__
char const* qnxnto = "INFO" ":" "qnxnto[]";
#endif
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]";
#endif
#define STRINGIFY_HELPER(X) #X
#define STRINGIFY(X) STRINGIFY_HELPER(X)
/* Identify known platforms by name. */
#if defined(__linux) || defined(__linux__) || defined(linux)
# define PLATFORM_ID "Linux"
#elif defined(__MSYS__)
# define PLATFORM_ID "MSYS"
#elif defined(__CYGWIN__)
# define PLATFORM_ID "Cygwin"
#elif defined(__MINGW32__)
# define PLATFORM_ID "MinGW"
#elif defined(__APPLE__)
# define PLATFORM_ID "Darwin"
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
# define PLATFORM_ID "Windows"
#elif defined(__FreeBSD__) || defined(__FreeBSD)
# define PLATFORM_ID "FreeBSD"
#elif defined(__NetBSD__) || defined(__NetBSD)
# define PLATFORM_ID "NetBSD"
#elif defined(__OpenBSD__) || defined(__OPENBSD)
# define PLATFORM_ID "OpenBSD"
#elif defined(__sun) || defined(sun)
# define PLATFORM_ID "SunOS"
#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__)
# define PLATFORM_ID "AIX"
#elif defined(__hpux) || defined(__hpux__)
# define PLATFORM_ID "HP-UX"
#elif defined(__HAIKU__)
# define PLATFORM_ID "Haiku"
#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS)
# define PLATFORM_ID "BeOS"
#elif defined(__QNX__) || defined(__QNXNTO__)
# define PLATFORM_ID "QNX"
#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__)
# define PLATFORM_ID "Tru64"
#elif defined(__riscos) || defined(__riscos__)
# define PLATFORM_ID "RISCos"
#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__)
# define PLATFORM_ID "SINIX"
#elif defined(__UNIX_SV__)
# define PLATFORM_ID "UNIX_SV"
#elif defined(__bsdos__)
# define PLATFORM_ID "BSDOS"
#elif defined(_MPRAS) || defined(MPRAS)
# define PLATFORM_ID "MP-RAS"
#elif defined(__osf) || defined(__osf__)
# define PLATFORM_ID "OSF1"
#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv)
# define PLATFORM_ID "SCO_SV"
#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX)
# define PLATFORM_ID "ULTRIX"
#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX)
# define PLATFORM_ID "Xenix"
#elif defined(__WATCOMC__)
# if defined(__LINUX__)
# define PLATFORM_ID "Linux"
# elif defined(__DOS__)
# define PLATFORM_ID "DOS"
# elif defined(__OS2__)
# define PLATFORM_ID "OS2"
# elif defined(__WINDOWS__)
# define PLATFORM_ID "Windows3x"
# elif defined(__VXWORKS__)
# define PLATFORM_ID "VxWorks"
# else /* unknown platform */
# define PLATFORM_ID
# endif
#elif defined(__INTEGRITY)
# if defined(INT_178B)
# define PLATFORM_ID "Integrity178"
# else /* regular Integrity */
# define PLATFORM_ID "Integrity"
# endif
# elif defined(_ADI_COMPILER)
# define PLATFORM_ID "ADSP"
#else /* unknown platform */
# define PLATFORM_ID
#endif
/* For windows compilers MSVC and Intel we can determine
the architecture of the compiler being used. This is because
the compilers do not have flags that can change the architecture,
but rather depend on which compiler is being used
*/
#if defined(_WIN32) && defined(_MSC_VER)
# if defined(_M_IA64)
# define ARCHITECTURE_ID "IA64"
# elif defined(_M_ARM64EC)
# define ARCHITECTURE_ID "ARM64EC"
# elif defined(_M_X64) || defined(_M_AMD64)
# define ARCHITECTURE_ID "x64"
# elif defined(_M_IX86)
# define ARCHITECTURE_ID "X86"
# elif defined(_M_ARM64)
# define ARCHITECTURE_ID "ARM64"
# elif defined(_M_ARM)
# if _M_ARM == 4
# define ARCHITECTURE_ID "ARMV4I"
# elif _M_ARM == 5
# define ARCHITECTURE_ID "ARMV5I"
# else
# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM)
# endif
# elif defined(_M_MIPS)
# define ARCHITECTURE_ID "MIPS"
# elif defined(_M_SH)
# define ARCHITECTURE_ID "SHx"
# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif
#elif defined(__WATCOMC__)
# if defined(_M_I86)
# define ARCHITECTURE_ID "I86"
# elif defined(_M_IX86)
# define ARCHITECTURE_ID "X86"
# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
# if defined(__ICCARM__)
# define ARCHITECTURE_ID "ARM"
# elif defined(__ICCRX__)
# define ARCHITECTURE_ID "RX"
# elif defined(__ICCRH850__)
# define ARCHITECTURE_ID "RH850"
# elif defined(__ICCRL78__)
# define ARCHITECTURE_ID "RL78"
# elif defined(__ICCRISCV__)
# define ARCHITECTURE_ID "RISCV"
# elif defined(__ICCAVR__)
# define ARCHITECTURE_ID "AVR"
# elif defined(__ICC430__)
# define ARCHITECTURE_ID "MSP430"
# elif defined(__ICCV850__)
# define ARCHITECTURE_ID "V850"
# elif defined(__ICC8051__)
# define ARCHITECTURE_ID "8051"
# elif defined(__ICCSTM8__)
# define ARCHITECTURE_ID "STM8"
# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif
#elif defined(__ghs__)
# if defined(__PPC64__)
# define ARCHITECTURE_ID "PPC64"
# elif defined(__ppc__)
# define ARCHITECTURE_ID "PPC"
# elif defined(__ARM__)
# define ARCHITECTURE_ID "ARM"
# elif defined(__x86_64__)
# define ARCHITECTURE_ID "x64"
# elif defined(__i386__)
# define ARCHITECTURE_ID "X86"
# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif
#elif defined(__clang__) && defined(__ti__)
# if defined(__ARM_ARCH)
# define ARCHITECTURE_ID "ARM"
# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif
#elif defined(__TI_COMPILER_VERSION__)
# if defined(__TI_ARM__)
# define ARCHITECTURE_ID "ARM"
# elif defined(__MSP430__)
# define ARCHITECTURE_ID "MSP430"
# elif defined(__TMS320C28XX__)
# define ARCHITECTURE_ID "TMS320C28x"
# elif defined(__TMS320C6X__) || defined(_TMS320C6X)
# define ARCHITECTURE_ID "TMS320C6x"
# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif
# elif defined(__ADSPSHARC__)
# define ARCHITECTURE_ID "SHARC"
# elif defined(__ADSPBLACKFIN__)
# define ARCHITECTURE_ID "Blackfin"
#elif defined(__TASKING__)
# if defined(__CTC__) || defined(__CPTC__)
# define ARCHITECTURE_ID "TriCore"
# elif defined(__CMCS__)
# define ARCHITECTURE_ID "MCS"
# elif defined(__CARM__)
# define ARCHITECTURE_ID "ARM"
# elif defined(__CARC__)
# define ARCHITECTURE_ID "ARC"
# elif defined(__C51__)
# define ARCHITECTURE_ID "8051"
# elif defined(__CPCP__)
# define ARCHITECTURE_ID "PCP"
# else
# define ARCHITECTURE_ID ""
# endif
#else
# define ARCHITECTURE_ID
#endif
/* Convert integer to decimal digit literals. */
#define DEC(n) \
('0' + (((n) / 10000000)%10)), \
('0' + (((n) / 1000000)%10)), \
('0' + (((n) / 100000)%10)), \
('0' + (((n) / 10000)%10)), \
('0' + (((n) / 1000)%10)), \
('0' + (((n) / 100)%10)), \
('0' + (((n) / 10)%10)), \
('0' + ((n) % 10))
/* Convert integer to hex digit literals. */
#define HEX(n) \
('0' + ((n)>>28 & 0xF)), \
('0' + ((n)>>24 & 0xF)), \
('0' + ((n)>>20 & 0xF)), \
('0' + ((n)>>16 & 0xF)), \
('0' + ((n)>>12 & 0xF)), \
('0' + ((n)>>8 & 0xF)), \
('0' + ((n)>>4 & 0xF)), \
('0' + ((n) & 0xF))
/* Construct a string literal encoding the version number. */
#ifdef COMPILER_VERSION
char const* info_version = "INFO" ":" "compiler_version[" COMPILER_VERSION "]";
/* Construct a string literal encoding the version number components. */
#elif defined(COMPILER_VERSION_MAJOR)
char const info_version[] = {
'I', 'N', 'F', 'O', ':',
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[',
COMPILER_VERSION_MAJOR,
# ifdef COMPILER_VERSION_MINOR
'.', COMPILER_VERSION_MINOR,
# ifdef COMPILER_VERSION_PATCH
'.', COMPILER_VERSION_PATCH,
# ifdef COMPILER_VERSION_TWEAK
'.', COMPILER_VERSION_TWEAK,
# endif
# endif
# endif
']','\0'};
#endif
/* Construct a string literal encoding the internal version number. */
#ifdef COMPILER_VERSION_INTERNAL
char const info_version_internal[] = {
'I', 'N', 'F', 'O', ':',
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_',
'i','n','t','e','r','n','a','l','[',
COMPILER_VERSION_INTERNAL,']','\0'};
#elif defined(COMPILER_VERSION_INTERNAL_STR)
char const* info_version_internal = "INFO" ":" "compiler_version_internal[" COMPILER_VERSION_INTERNAL_STR "]";
#endif
/* Construct a string literal encoding the version number components. */
#ifdef SIMULATE_VERSION_MAJOR
char const info_simulate_version[] = {
'I', 'N', 'F', 'O', ':',
's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[',
SIMULATE_VERSION_MAJOR,
# ifdef SIMULATE_VERSION_MINOR
'.', SIMULATE_VERSION_MINOR,
# ifdef SIMULATE_VERSION_PATCH
'.', SIMULATE_VERSION_PATCH,
# ifdef SIMULATE_VERSION_TWEAK
'.', SIMULATE_VERSION_TWEAK,
# endif
# endif
# endif
']','\0'};
#endif
/* Construct the string literal in pieces to prevent the source from
getting matched. Store it in a pointer rather than an array
because some compilers will just produce instructions to fill the
array rather than assigning a pointer to a static array. */
char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]";
char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]";
#define C_STD_99 199901L
#define C_STD_11 201112L
#define C_STD_17 201710L
#define C_STD_23 202311L
#ifdef __STDC_VERSION__
# define C_STD __STDC_VERSION__
#endif
#if !defined(__STDC__) && !defined(__clang__)
# if defined(_MSC_VER) || defined(__ibmxl__) || defined(__IBMC__)
# define C_VERSION "90"
# else
# define C_VERSION
# endif
#elif C_STD > C_STD_17
# define C_VERSION "23"
#elif C_STD > C_STD_11
# define C_VERSION "17"
#elif C_STD > C_STD_99
# define C_VERSION "11"
#elif C_STD >= C_STD_99
# define C_VERSION "99"
#else
# define C_VERSION "90"
#endif
const char* info_language_standard_default =
"INFO" ":" "standard_default[" C_VERSION "]";
const char* info_language_extensions_default = "INFO" ":" "extensions_default["
#if (defined(__clang__) || defined(__GNUC__) || defined(__xlC__) || \
defined(__TI_COMPILER_VERSION__)) && \
!defined(__STRICT_ANSI__)
"ON"
#else
"OFF"
#endif
"]";
/*--------------------------------------------------------------------------*/
#ifdef ID_VOID_MAIN
void main() {}
#else
# if defined(__CLASSIC_C__)
int main(argc, argv) int argc; char *argv[];
# else
int main(int argc, char* argv[])
# endif
{
int require = 0;
require += info_compiler[argc];
require += info_platform[argc];
require += info_arch[argc];
#ifdef COMPILER_VERSION_MAJOR
require += info_version[argc];
#endif
#ifdef COMPILER_VERSION_INTERNAL
require += info_version_internal[argc];
#endif
#ifdef SIMULATE_ID
require += info_simulate[argc];
#endif
#ifdef SIMULATE_VERSION_MAJOR
require += info_simulate_version[argc];
#endif
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
require += info_cray[argc];
#endif
require += info_language_standard_default[argc];
require += info_language_extensions_default[argc];
(void)argv;
return require;
}
#endif

View File

@@ -0,0 +1,283 @@
---
events:
-
kind: "message-v1"
backtrace:
- "/usr/share/cmake-3.31/Modules/CMakeDetermineSystem.cmake:205 (message)"
- "CMakeLists.txt:2 (project)"
message: |
The system is: Linux - 6.12.59-1.qubes.fc41.x86_64 - x86_64
-
kind: "message-v1"
backtrace:
- "/usr/share/cmake-3.31/Modules/CMakeDetermineCompilerId.cmake:17 (message)"
- "/usr/share/cmake-3.31/Modules/CMakeDetermineCompilerId.cmake:64 (__determine_compiler_id_test)"
- "/usr/share/cmake-3.31/Modules/CMakeDetermineCCompiler.cmake:123 (CMAKE_DETERMINE_COMPILER_ID)"
- "CMakeLists.txt:2 (project)"
message: |
Compiling the C compiler identification source file "CMakeCCompilerId.c" succeeded.
Compiler: /usr/bin/cc
Build flags:
Id flags:
The output was:
0
Compilation of the C compiler identification source "CMakeCCompilerId.c" produced "a.out"
The C compiler identification is GNU, found in:
/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/3.31.6/CompilerIdC/a.out
-
kind: "try_compile-v1"
backtrace:
- "/usr/share/cmake-3.31/Modules/CMakeDetermineCompilerABI.cmake:74 (try_compile)"
- "/usr/share/cmake-3.31/Modules/CMakeTestCCompiler.cmake:26 (CMAKE_DETERMINE_COMPILER_ABI)"
- "CMakeLists.txt:2 (project)"
checks:
- "Detecting C compiler ABI info"
directories:
source: "/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/CMakeScratch/TryCompile-4YXQzO"
binary: "/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/CMakeScratch/TryCompile-4YXQzO"
cmakeVariables:
CMAKE_C_FLAGS: ""
CMAKE_C_FLAGS_DEBUG: "-g"
CMAKE_EXE_LINKER_FLAGS: ""
buildResult:
variable: "CMAKE_C_ABI_COMPILED"
cached: true
stdout: |
Change Dir: '/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/CMakeScratch/TryCompile-4YXQzO'
Run Build Command(s): /usr/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_54cf1/fast
/usr/bin/gmake -f CMakeFiles/cmTC_54cf1.dir/build.make CMakeFiles/cmTC_54cf1.dir/build
gmake[1]: Entering directory '/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/CMakeScratch/TryCompile-4YXQzO'
Building C object CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o
/usr/bin/cc -v -o CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o -c /usr/share/cmake-3.31/Modules/CMakeCCompilerABI.c
Using built-in specs.
COLLECT_GCC=/usr/bin/cc
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 14.2.0-19' --with-bugurl=file:///usr/share/doc/gcc-14/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust --prefix=/usr --with-gcc-major-version-only --program-suffix=-14 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=3
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 14.2.0 (Debian 14.2.0-19)
COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_54cf1.dir/'
/usr/libexec/gcc/x86_64-linux-gnu/14/cc1 -quiet -v -imultiarch x86_64-linux-gnu /usr/share/cmake-3.31/Modules/CMakeCCompilerABI.c -quiet -dumpdir CMakeFiles/cmTC_54cf1.dir/ -dumpbase CMakeCCompilerABI.c.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -o /tmp/ccoPdBhv.s
GNU C17 (Debian 14.2.0-19) version 14.2.0 (x86_64-linux-gnu)
compiled by GNU C version 14.2.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.27-GMP
warning: MPFR header version 4.2.1 differs from library version 4.2.2.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/14/include-fixed/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/14/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/14/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
Compiler executable checksum: 0051dfc1fa4e89ba97760da93f2546f3
COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_54cf1.dir/'
as -v --64 -o CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o /tmp/ccoPdBhv.s
GNU assembler version 2.44 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.44
COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.'
Linking C executable cmTC_54cf1
/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_54cf1.dir/link.txt --verbose=1
Using built-in specs.
COLLECT_GCC=/usr/bin/cc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 14.2.0-19' --with-bugurl=file:///usr/share/doc/gcc-14/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust --prefix=/usr --with-gcc-major-version-only --program-suffix=-14 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=3
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 14.2.0 (Debian 14.2.0-19)
COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_54cf1' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_54cf1.'
/usr/libexec/gcc/x86_64-linux-gnu/14/collect2 -plugin /usr/libexec/gcc/x86_64-linux-gnu/14/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper -plugin-opt=-fresolution=/tmp/ccS3zq4v.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_54cf1 /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/14 -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/14/../../.. -v CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crtn.o
collect2 version 14.2.0
/usr/bin/ld -plugin /usr/libexec/gcc/x86_64-linux-gnu/14/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper -plugin-opt=-fresolution=/tmp/ccS3zq4v.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_54cf1 /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/14 -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/14/../../.. -v CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crtn.o
GNU ld (GNU Binutils for Debian) 2.44
COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_54cf1' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_54cf1.'
/usr/bin/cc -v -Wl,-v CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o -o cmTC_54cf1
gmake[1]: Leaving directory '/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/CMakeScratch/TryCompile-4YXQzO'
exitCode: 0
-
kind: "message-v1"
backtrace:
- "/usr/share/cmake-3.31/Modules/CMakeDetermineCompilerABI.cmake:182 (message)"
- "/usr/share/cmake-3.31/Modules/CMakeTestCCompiler.cmake:26 (CMAKE_DETERMINE_COMPILER_ABI)"
- "CMakeLists.txt:2 (project)"
message: |
Parsed C implicit include dir info: rv=done
found start of include info
found start of implicit include info
add: [/usr/lib/gcc/x86_64-linux-gnu/14/include]
add: [/usr/local/include]
add: [/usr/include/x86_64-linux-gnu]
add: [/usr/include]
end of search list found
collapse include dir [/usr/lib/gcc/x86_64-linux-gnu/14/include] ==> [/usr/lib/gcc/x86_64-linux-gnu/14/include]
collapse include dir [/usr/local/include] ==> [/usr/local/include]
collapse include dir [/usr/include/x86_64-linux-gnu] ==> [/usr/include/x86_64-linux-gnu]
collapse include dir [/usr/include] ==> [/usr/include]
implicit include dirs: [/usr/lib/gcc/x86_64-linux-gnu/14/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include]
-
kind: "message-v1"
backtrace:
- "/usr/share/cmake-3.31/Modules/CMakeDetermineCompilerABI.cmake:218 (message)"
- "/usr/share/cmake-3.31/Modules/CMakeTestCCompiler.cmake:26 (CMAKE_DETERMINE_COMPILER_ABI)"
- "CMakeLists.txt:2 (project)"
message: |
Parsed C implicit link information:
link line regex: [^( *|.*[/\\])(ld[0-9]*(\\.[a-z]+)?|CMAKE_LINK_STARTFILE-NOTFOUND|([^/\\]+-)?ld|collect2)[^/\\]*( |$)]
linker tool regex: [^[ ]*(->|")?[ ]*(([^"]*[/\\])?(ld[0-9]*(\\.[a-z]+)?))("|,| |$)]
ignore line: [Change Dir: '/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/CMakeScratch/TryCompile-4YXQzO']
ignore line: []
ignore line: [Run Build Command(s): /usr/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_54cf1/fast]
ignore line: [/usr/bin/gmake -f CMakeFiles/cmTC_54cf1.dir/build.make CMakeFiles/cmTC_54cf1.dir/build]
ignore line: [gmake[1]: Entering directory '/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/CMakeScratch/TryCompile-4YXQzO']
ignore line: [Building C object CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o]
ignore line: [/usr/bin/cc -v -o CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o -c /usr/share/cmake-3.31/Modules/CMakeCCompilerABI.c]
ignore line: [Using built-in specs.]
ignore line: [COLLECT_GCC=/usr/bin/cc]
ignore line: [OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa]
ignore line: [OFFLOAD_TARGET_DEFAULT=1]
ignore line: [Target: x86_64-linux-gnu]
ignore line: [Configured with: ../src/configure -v --with-pkgversion='Debian 14.2.0-19' --with-bugurl=file:///usr/share/doc/gcc-14/README.Bugs --enable-languages=c ada c++ go d fortran objc obj-c++ m2 rust --prefix=/usr --with-gcc-major-version-only --program-suffix=-14 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32 m64 mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-nvptx/usr amdgcn-amdhsa=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=3]
ignore line: [Thread model: posix]
ignore line: [Supported LTO compression algorithms: zlib zstd]
ignore line: [gcc version 14.2.0 (Debian 14.2.0-19) ]
ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_54cf1.dir/']
ignore line: [ /usr/libexec/gcc/x86_64-linux-gnu/14/cc1 -quiet -v -imultiarch x86_64-linux-gnu /usr/share/cmake-3.31/Modules/CMakeCCompilerABI.c -quiet -dumpdir CMakeFiles/cmTC_54cf1.dir/ -dumpbase CMakeCCompilerABI.c.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -o /tmp/ccoPdBhv.s]
ignore line: [GNU C17 (Debian 14.2.0-19) version 14.2.0 (x86_64-linux-gnu)]
ignore line: [ compiled by GNU C version 14.2.0 GMP version 6.3.0 MPFR version 4.2.1 MPC version 1.3.1 isl version isl-0.27-GMP]
ignore line: []
ignore line: [warning: MPFR header version 4.2.1 differs from library version 4.2.2.]
ignore line: [GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072]
ignore line: [ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"]
ignore line: [ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/14/include-fixed/x86_64-linux-gnu"]
ignore line: [ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/14/include-fixed"]
ignore line: [ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include"]
ignore line: [#include "..." search starts here:]
ignore line: [#include <...> search starts here:]
ignore line: [ /usr/lib/gcc/x86_64-linux-gnu/14/include]
ignore line: [ /usr/local/include]
ignore line: [ /usr/include/x86_64-linux-gnu]
ignore line: [ /usr/include]
ignore line: [End of search list.]
ignore line: [Compiler executable checksum: 0051dfc1fa4e89ba97760da93f2546f3]
ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_54cf1.dir/']
ignore line: [ as -v --64 -o CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o /tmp/ccoPdBhv.s]
ignore line: [GNU assembler version 2.44 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.44]
ignore line: [COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/]
ignore line: [LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../:/lib/:/usr/lib/]
ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.']
ignore line: [Linking C executable cmTC_54cf1]
ignore line: [/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_54cf1.dir/link.txt --verbose=1]
ignore line: [Using built-in specs.]
ignore line: [COLLECT_GCC=/usr/bin/cc]
ignore line: [COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper]
ignore line: [OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa]
ignore line: [OFFLOAD_TARGET_DEFAULT=1]
ignore line: [Target: x86_64-linux-gnu]
ignore line: [Configured with: ../src/configure -v --with-pkgversion='Debian 14.2.0-19' --with-bugurl=file:///usr/share/doc/gcc-14/README.Bugs --enable-languages=c ada c++ go d fortran objc obj-c++ m2 rust --prefix=/usr --with-gcc-major-version-only --program-suffix=-14 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32 m64 mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-nvptx/usr amdgcn-amdhsa=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=3]
ignore line: [Thread model: posix]
ignore line: [Supported LTO compression algorithms: zlib zstd]
ignore line: [gcc version 14.2.0 (Debian 14.2.0-19) ]
ignore line: [COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/14/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/]
ignore line: [LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/14/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/14/../../../:/lib/:/usr/lib/]
ignore line: [COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_54cf1' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_54cf1.']
link line: [ /usr/libexec/gcc/x86_64-linux-gnu/14/collect2 -plugin /usr/libexec/gcc/x86_64-linux-gnu/14/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper -plugin-opt=-fresolution=/tmp/ccS3zq4v.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_54cf1 /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/14 -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/14/../../.. -v CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crtn.o]
arg [/usr/libexec/gcc/x86_64-linux-gnu/14/collect2] ==> ignore
arg [-plugin] ==> ignore
arg [/usr/libexec/gcc/x86_64-linux-gnu/14/liblto_plugin.so] ==> ignore
arg [-plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper] ==> ignore
arg [-plugin-opt=-fresolution=/tmp/ccS3zq4v.res] ==> ignore
arg [-plugin-opt=-pass-through=-lgcc] ==> ignore
arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore
arg [-plugin-opt=-pass-through=-lc] ==> ignore
arg [-plugin-opt=-pass-through=-lgcc] ==> ignore
arg [-plugin-opt=-pass-through=-lgcc_s] ==> ignore
arg [--build-id] ==> ignore
arg [--eh-frame-hdr] ==> ignore
arg [-m] ==> ignore
arg [elf_x86_64] ==> ignore
arg [--hash-style=gnu] ==> ignore
arg [--as-needed] ==> ignore
arg [-dynamic-linker] ==> ignore
arg [/lib64/ld-linux-x86-64.so.2] ==> ignore
arg [-pie] ==> ignore
arg [-o] ==> ignore
arg [cmTC_54cf1] ==> ignore
arg [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o] ==> obj [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o]
arg [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o] ==> obj [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o]
arg [/usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o] ==> obj [/usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o]
arg [-L/usr/lib/gcc/x86_64-linux-gnu/14] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/14]
arg [-L/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu]
arg [-L/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib]
arg [-L/lib/x86_64-linux-gnu] ==> dir [/lib/x86_64-linux-gnu]
arg [-L/lib/../lib] ==> dir [/lib/../lib]
arg [-L/usr/lib/x86_64-linux-gnu] ==> dir [/usr/lib/x86_64-linux-gnu]
arg [-L/usr/lib/../lib] ==> dir [/usr/lib/../lib]
arg [-L/usr/lib/gcc/x86_64-linux-gnu/14/../../..] ==> dir [/usr/lib/gcc/x86_64-linux-gnu/14/../../..]
arg [-v] ==> ignore
arg [CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o] ==> ignore
arg [-lgcc] ==> lib [gcc]
arg [--push-state] ==> ignore
arg [--as-needed] ==> ignore
arg [-lgcc_s] ==> lib [gcc_s]
arg [--pop-state] ==> ignore
arg [-lc] ==> lib [c]
arg [-lgcc] ==> lib [gcc]
arg [--push-state] ==> ignore
arg [--as-needed] ==> ignore
arg [-lgcc_s] ==> lib [gcc_s]
arg [--pop-state] ==> ignore
arg [/usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o] ==> obj [/usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o]
arg [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crtn.o] ==> obj [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crtn.o]
ignore line: [collect2 version 14.2.0]
ignore line: [/usr/bin/ld -plugin /usr/libexec/gcc/x86_64-linux-gnu/14/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper -plugin-opt=-fresolution=/tmp/ccS3zq4v.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_54cf1 /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/14 -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/14/../../.. -v CMakeFiles/cmTC_54cf1.dir/CMakeCCompilerABI.c.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crtn.o]
linker tool for 'C': /usr/bin/ld
collapse obj [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o] ==> [/usr/lib/x86_64-linux-gnu/Scrt1.o]
collapse obj [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o] ==> [/usr/lib/x86_64-linux-gnu/crti.o]
collapse obj [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crtn.o] ==> [/usr/lib/x86_64-linux-gnu/crtn.o]
collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/14] ==> [/usr/lib/gcc/x86_64-linux-gnu/14]
collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu]
collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/14/../../../../lib] ==> [/usr/lib]
collapse library dir [/lib/x86_64-linux-gnu] ==> [/lib/x86_64-linux-gnu]
collapse library dir [/lib/../lib] ==> [/lib]
collapse library dir [/usr/lib/x86_64-linux-gnu] ==> [/usr/lib/x86_64-linux-gnu]
collapse library dir [/usr/lib/../lib] ==> [/usr/lib]
collapse library dir [/usr/lib/gcc/x86_64-linux-gnu/14/../../..] ==> [/usr/lib]
implicit libs: [gcc;gcc_s;c;gcc;gcc_s]
implicit objs: [/usr/lib/x86_64-linux-gnu/Scrt1.o;/usr/lib/x86_64-linux-gnu/crti.o;/usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o;/usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o;/usr/lib/x86_64-linux-gnu/crtn.o]
implicit dirs: [/usr/lib/gcc/x86_64-linux-gnu/14;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib]
implicit fwks: []
-
kind: "message-v1"
backtrace:
- "/usr/share/cmake-3.31/Modules/Internal/CMakeDetermineLinkerId.cmake:40 (message)"
- "/usr/share/cmake-3.31/Modules/CMakeDetermineCompilerABI.cmake:255 (cmake_determine_linker_id)"
- "/usr/share/cmake-3.31/Modules/CMakeTestCCompiler.cmake:26 (CMAKE_DETERMINE_COMPILER_ABI)"
- "CMakeLists.txt:2 (project)"
message: |
Running the C compiler's linker: "/usr/bin/ld" "-v"
GNU ld (GNU Binutils for Debian) 2.44
...

View File

@@ -0,0 +1,16 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.31
# Relative path conversion top directories.
set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/user/lt/n_os_tr/stack/nostr-id-tui")
set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host")
# Force unix paths in dependencies.
set(CMAKE_FORCE_UNIX_PATHS 1)
# The C and CXX include file regular expressions for this directory.
set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$")
set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$")
set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN})
set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN})

Some files were not shown because too many files have changed in this diff Show More