v0.0.2 - spinning out n_signer

This commit is contained in:
Laan Tungir
2026-05-02 09:21:19 -04:00
parent faf2838632
commit adebb27c62
34 changed files with 2074 additions and 310 deletions

1
includes/nips Submodule

Submodule includes/nips added at 05d3f198c6

View File

@@ -1,26 +1,35 @@
#!/bin/sh
# Enable n-OS-tr services so they start automatically on boot.
set -e
# Configure boot-time enable/disable posture for n-OS-tr services.
set -eu
# Disable the Debian default site so it doesn't conflict with ours.
rm -f /etc/nginx/sites-enabled/default
# Enable our site.
# Enable our site symlink only. Runtime nginx startup is user-selected in TUI.
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
# TTY handoff: identity TUI owns tty1 before getty.
systemctl disable getty@tty1.service || true
# Always-on boot pipeline.
systemctl enable n-os-tr-firstboot.service
systemctl enable nostr-id.service || true
systemctl enable nostr-id-tui.service
# Explicitly keep optional services disabled at boot.
for unit in \
c-relay.service \
ginxsom.service \
fips.service \
fips-dns.service \
nginx.service \
nostr-config-loader.service \
nostr-config-writer.service \
ssh.service \
tor.service
do
systemctl disable "$unit" 2>/dev/null || true
done
# Apply preset policy for any additional units.
systemctl preset-all --preset-mode=enable-only 2>/dev/null || true

View File

@@ -0,0 +1,9 @@
# key\tunit\tlabel\tdefault\tdeps
r c-relay.service c-relay off nginx.service
g ginxsom.service ginxsom off nginx.service
f fips.service fips off -
n nginx.service nginx off -
l nostr-config-loader.service config-loader off -
w nostr-config-writer.service config-writer off nostr-config-loader.service
s ssh.service ssh off -
t tor.service tor off -

View File

@@ -0,0 +1,16 @@
# n-OS-tr service posture: only identity + firstboot + TUI start automatically.
# Optional services are loaded/installed but disabled by default.
enable n-os-tr-firstboot.service
enable nostr-id.service
enable nostr-id-tui.service
disable c-relay.service
disable ginxsom.service
disable fips.service
disable fips-dns.service
disable nginx.service
disable nostr-config-loader.service
disable nostr-config-writer.service
disable ssh.service
disable tor.service

View File

@@ -15,3 +15,6 @@ nftables
# TUI runtime dependency
libncursesw6
# Service-menu optional stack
tor

13
n_os_tr.code-workspace Normal file
View File

@@ -0,0 +1,13 @@
{
"folders": [
{
"path": "."
},
{
"path": "../aesthetics"
}
],
"settings": {
"git.ignoreLimitWarning": true
}
}

View File

@@ -7,12 +7,13 @@ survive individual work sessions.
## 1. Big-picture goal
A single bootable/installable Debian Live ISO that, the moment it finishes
booting, is running:
booting, has all n-OS-tr services installed and configured, then presents a
boot-time identity + service-selection TUI before login:
- [`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
- [`fips`](../includes/fips) — Rust mesh-routing daemon, npub-addressed IPv6 overlay (optional start)
- [`c-relay`](../includes/c-relay) — C Nostr relay with event-based config and embedded admin (optional start)
- [`ginxsom`](../includes/ginxsom) — C Blossom (blob) server as FastCGI behind nginx (optional start)
- `nginx` — TLS front door, static blob serving, reverse proxy (optional start)
No Docker. No `curl | bash` provisioning. All three apps run as native
systemd services directly on the host.
@@ -116,28 +117,46 @@ sequenceDiagram
participant LB as live-boot / initramfs
participant Sd as systemd PID 1
participant Fb as n-os-tr-firstboot.service
participant IA as nostr-id.service
participant TUI as nostr-id-tui.service
participant Fp as fips.service
participant Cr as c-relay.service
participant Gx as ginxsom.service
participant Ng as nginx.service
participant GT as getty@tty1.service
LB->>Sd: hand off
Sd->>Fb: start oneshot (Before= the app units)
Sd->>Fb: start oneshot (Before= identity + 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)
Sd->>IA: start identity-agent
IA-->>Sd: active (control socket ready)
Sd->>TUI: start boot-time identity + services flow on tty1
TUI->>IA: load_mnemonic / load_bunker / load_amnesia
IA-->>TUI: identity loaded
TUI->>Sd: start/stop selected services via systemctl
opt User enabled nginx
Sd->>Ng: start
end
opt User enabled c-relay
Sd->>Cr: start
end
opt User enabled ginxsom
Sd->>Gx: start
end
opt User enabled fips
Sd->>Fp: start
end
TUI->>Sd: exit 0
Sd->>GT: start (after TUI exits)
```
## 5. Phase 0.A — Gitea artifact fetch

View File

@@ -185,6 +185,10 @@ stack/nostr-id-tui/ # new subtree
│ ├── 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_services.c # post-identity service selection menu
│ ├── screen_services_confirm.c # commit/apply status + failure handling
│ ├── services_conf.c # /etc/n-os-tr/services.conf parser
│ ├── systemd_client.c # wrapper over /bin/systemctl and journalctl
│ ├── 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)
@@ -237,9 +241,12 @@ stateDiagram-v2
Amnesia --> SendAgentAmnesia
SendAgent --> Exit: agent returns ok
SendAgent --> Services: agent returns ok
SendAgent --> FatalError: agent returns error
SendAgentAmnesia --> Exit
SendAgentAmnesia --> Services
Services --> Exit: commit/skip complete
Services --> FatalError: unrecoverable apply error
FatalError --> [*]
Exit --> [*]
@@ -261,7 +268,9 @@ stateDiagram-v2
**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).
**Services.** After identity load succeeds, the same TUI presents a service-selection screen (see [`plans/tui_service_menu.md`](tui_service_menu.md)). The user toggles optional services (c-relay, ginxsom, fips, nginx, config-loader, config-writer, ssh, tor) and either commits changes or skips with all services off for that session.
**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, memory locking failed (which means the threat-model invariants cannot be enforced), or service-apply hits a non-recoverable system state.
### 3.3 Anti-shoulder-surf tradeoffs
@@ -408,6 +417,9 @@ sequenceDiagram
Note over TUI: user interacts
TUI->>IA: load_mnemonic / load_bunker / load_amnesia
IA-->>TUI: ok
Note over TUI: service selection menu (same binary)
TUI->>SD: start/stop selected units
SD-->>TUI: unit results
TUI->>SD: exit 0
SD->>GT: start (now allowed by ordering)
GT->>User: "n-os-tr login:"

538
plans/tui_service_menu.md Normal file
View File

@@ -0,0 +1,538 @@
# TUI Service Menu — Post-Identity Service Selection
> **Status:** Design. This document extends [`plans/tui_login.md`](tui_login.md) with a second screen inside the same `nostr-id-tui` binary: after the user has successfully loaded an identity (mnemonic / generated / imported / bunker / amnesia), the TUI presents a menu of **optional services** and lets the user toggle each one on or off **before** `getty@tty1` is allowed to start.
>
> Depends on: [`plans/tui_login.md`](tui_login.md), [`plans/identity_subsystem.md`](identity_subsystem.md), [`plans/iso_architecture.md`](iso_architecture.md), [`plans/nostr_config_projection.md`](nostr_config_projection.md), [`/home/user/lt/aesthetics/TUI.md`](/home/user/lt/aesthetics/TUI.md).
## 0. Policy
1. **n-OS-tr always installs and sets up every service.** Binaries, unit files, config templates, data directories — all present on the image.
2. **No service starts automatically at boot except the identity/boot pipeline itself.** Exactly these three are enabled image-wide: [`n-os-tr-firstboot.service`](../iso/config/includes.chroot/etc/systemd/system/n-os-tr-firstboot.service), `nostr-id.service` (identity-agent), [`nostr-id-tui.service`](../iso/config/includes.chroot/etc/systemd/system/nostr-id-tui.service). Everything else ships `disabled` via the live-build preset policy.
3. **The user decides what turns on.** After the user authenticates via `nostr-id-tui`, the TUI presents a service-toggle screen. The user picks what they want running this session.
4. **Selections are session-local in v1.** No persistence across reboots. Every boot starts with the same defaults. A future revision may project selections to a Nostr config slot (§10).
5. **One binary, one ceremony.** The service menu is a new screen inside `nostr-id-tui`, not a separate unit. Rationale discussed in the architect session: the user is already in one authenticated terminal flow; we do not want two back-to-back ceremonies before login.
6. **Default-off by default.** Every toggleable service starts the session in the "off" state. The user must explicitly enable each one they want running. This matches the privacy-forward posture of the project.
7. **Style:** strict adherence to [`/home/user/lt/aesthetics/TUI.md`](/home/user/lt/aesthetics/TUI.md) — single-key actions, terminal-width headers, ANSI underline for hotkeys, canonical menu template (§12 of that doc).
## 1. Scope
### 1.1 Services in the menu (v1)
| Key | Label | systemd unit | Default | What it does |
|-----|-----------------|---------------------------|---------|--------------------------------------------------------|
| `r` | `c-relay` | `c-relay.service` | off | Local Nostr relay (NIP-01/42). Admin key = index 2. |
| `g` | `ginxsom` | `ginxsom.service` | off | Blossom blob server behind nginx. Server key = index 3.|
| `f` | `fips` | `fips.service` | off | Mesh-routing daemon; npub-IPv6 overlay. Key = index 4. |
| `n` | `nginx` | `nginx.service` | off | TLS front door / reverse proxy. Needed by c-relay ws, ginxsom uploads. |
| `l` | `config-loader` | `nostr-config-loader.service` | off | Fetch + materialize user config slots (§ [`nostr_config_projection.md`](nostr_config_projection.md#3-boot-flow)). |
| `w` | `config-writer` | `nostr-config-writer.service` | off | Save tracked home paths back to Nostr on shutdown. |
| `s` | `ssh` | `ssh.service` | off | OpenSSH server. Keys regenerated at firstboot. |
| `t` | `tor` | `tor.service` | off | Tor daemon. Default posture = SOCKS-only (client), not exit/relay. |
### 1.2 Services explicitly NOT in the menu
- `nostr-id.service` — identity-agent is always required; it's the thing the TUI talks to.
- `n-os-tr-firstboot.service` — oneshot, runs before the TUI, not user-controlled.
- `nostr-id-tui.service` — the TUI itself.
- `systemd-networkd`/`NetworkManager`/`dbus` — core system plumbing; managed by distro defaults.
### 1.3 Service dependencies the TUI must respect
Some services only make sense together. The TUI enforces these dependencies at toggle time (not at the systemd level — systemd's own `Requires=`/`Wants=` handles the actual startup ordering):
| Service | Implicit deps (auto-enabled when this is turned on) |
|---------------|-------------------------------------------------------------|
| `c-relay` | `nginx` (wss termination and cert) |
| `ginxsom` | `nginx` (serves blobs, proxies uploads) |
| `config-writer` | `config-loader` (must have run at boot to know the slots) |
If the user toggles `c-relay` on and `nginx` is off, the TUI:
- Shows a one-line prompt: `c-relay needs nginx. Turn nginx on too? [Y/n]`.
- Default `y` on Enter. If `n`, the toggle is cancelled (we do not start a service that won't be reachable).
Reverse direction (turning `nginx` off while `c-relay` is on): prompt `c-relay will become unreachable without nginx. Stop c-relay too? [Y/n]`.
## 2. Placement in the existing TUI flow
### 2.1 Updated 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
Enter --> SendAgent: BIP-39 ok
Generate --> SendAgent: confirm words ok
Import --> SendAgent: file read + wiped
Bunker --> SendAgent: NIP-46 connected
Amnesia --> SendAgent: ephemeral key
SendAgent --> Services: agent returns ok
SendAgent --> FatalError: agent returns error
Services --> Services: user toggles
Services --> Confirm: user presses C (commit)
Services --> Services: user presses X (skip, all off)
Confirm --> Apply: user presses Y
Confirm --> Services: user presses N
Apply --> Apply: start/stop each selected unit
Apply --> Exit: all units reached terminal state
Apply --> ApplyError: any unit failed
ApplyError --> Services: user reviews, retries
FatalError --> [*]
Exit --> [*]
```
The only new states are `Services`, `Confirm`, `Apply`, and `ApplyError`. Everything before `SendAgent` is unchanged from [`plans/tui_login.md §3.1`](tui_login.md#31-state-diagram).
### 2.2 Updated boot sequence
```mermaid
sequenceDiagram
participant SD as systemd
participant IA as nostr-id.service
participant TUI as nostr-id-tui.service
participant Svc as toggleable services
participant GT as getty@tty1.service
SD->>IA: start
IA-->>SD: listening on control.sock
SD->>TUI: start (owns tty1)
Note over TUI: identity screens
TUI->>IA: load_mnemonic/bunker/amnesia
IA-->>TUI: ok, main_npub
Note over TUI: service menu
TUI->>TUI: user toggles, presses Commit
TUI->>SD: StartUnit/StopUnit via dbus
SD->>Svc: start selected services
Svc-->>SD: active
SD-->>TUI: job results
TUI->>SD: exit 0
SD->>GT: start (ordering allows)
GT->>User: n-os-tr login:
```
## 3. Screen design
Strict compliance with [`/home/user/lt/aesthetics/TUI.md`](/home/user/lt/aesthetics/TUI.md). Every screen follows the canonical template in §12 of that doc.
### 3.1 Main services screen
```text
===================== SERVICES =====================
(expands to terminal width)
Identity: npub1abcd…wxyz (main, index 0)
Session: live (in-memory, no persistence)
Key Service State Default Depends on
--- ------------- --------- ------- -----------
R c-relay [ off ] off nginx
G ginxsom [ off ] off nginx
F fips [ off ] off —
N nginx [ off ] off —
L config-loader [ off ] off —
W config-writer [ off ] off config-loader
S ssh [ off ] off —
T tor [ off ] off —
Actions:
R/G/F/N/L/W/S/T) Toggle service
A) Turn All on
Z) Turn all off (reset)
C) Commit and continue
X) Skip (all off, continue)
Select action: >
```
Formatting rules (all from [TUI.md](/home/user/lt/aesthetics/TUI.md)):
- Header expands to full terminal width using the `=== SERVICES ===` pattern from [`ui.c:print_centered_header`](/home/user/lt/aesthetics/TUI.md:88).
- Column widths are fixed; when terminal is narrower than 60 cols, drop the "Default" and "Depends on" columns (the state is what matters).
- State column is either `[ off ]` or `[ on ]`. A third transient state `[ ... ]` appears during the Apply phase.
- The first letter of each service name in the Key column is rendered with ANSI underline via `\033[4m<letter>\033[0m`, matching [TUI.md §7](/home/user/lt/aesthetics/TUI.md).
- Prompt at bottom is `Select action: >` per the canonical template.
### 3.2 Confirm screen
Before actually doing anything, we show what will happen:
```text
===================== CONFIRM =====================
The following changes will be applied:
START nginx.service
START c-relay.service
START fips.service
(5 services remain off)
Proceed? [Y/n] >
```
Only services whose session state differs from their default appear. Pure-default sessions (no changes) go straight from the services screen's `C`/`X` directly to `Apply` with an empty plan and exit.
### 3.3 Apply screen
Live progress. We iterate the plan serially (not concurrently — systemd handles that anyway, and serial output is easier to read):
```text
===================== APPLYING =====================
[ OK ] nginx.service started in 0.42s
[ OK ] c-relay.service started in 1.14s
[ ... ] fips.service starting...
[ ] (waiting)
Press Ctrl-C to abort remaining jobs (already-started services stay up).
```
Each row shows one of:
- `[ ]` queued
- `[ ... ]` in progress (animated dot after 250 ms; ANSI cursor save/restore to re-render only the affected row)
- `[ OK ]` finished successfully
- `[FAIL ]` finished with an error
- `[SKIP ]` not applied (e.g. dependency refused earlier)
After all jobs complete:
- All green → advance to Exit immediately (print `All services started. Handing control to login...` for 1 second, then the TUI exits and getty takes over).
- Any failures → advance to **ApplyError** screen.
### 3.4 Apply-error screen
```text
===================== PARTIAL FAILURE =====================
Some services did not start cleanly:
[FAIL ] ginxsom.service
SubState: failed
ExecMainStatus: 1
Last log line: "/usr/local/bin/ginxsom/ginxsom-fcgi: socket bind failed"
Actions:
V) View journal for failed services
R) Retry failed services only
B) Back to service menu
I) Ignore and continue to login anyway
Select action: >
```
The journal view (`V`) shells out to `journalctl -u <unit> -n 50 --no-pager` via a pipe (we do not embed a log viewer). `less`-style paging is acceptable if available; otherwise we just print and prompt to continue.
## 4. Service manifest
The TUI does not hard-code the service list in C. It reads `/etc/n-os-tr/services.conf` at startup. This lets us add/remove services (especially tor, which lands later) without rebuilding the binary.
### 4.1 File format
A small line-based format, one record per service, fields tab-separated:
```
# key unit label default deps
r c-relay.service c-relay off nginx.service
g ginxsom.service ginxsom off nginx.service
f fips.service fips off -
n nginx.service nginx off -
l nostr-config-loader.service config-loader off -
w nostr-config-writer.service config-writer off nostr-config-loader.service
s ssh.service ssh off -
t tor.service tor off -
```
Comments start with `#`. Blank lines ignored. Unknown fields ignored for forward compatibility. Multiple deps separated by `,`. A literal `-` means no deps.
Parsing happens in `stack/nostr-id-tui/src/services_conf.c`. On parse error the TUI falls back to a hardcoded minimal list (c-relay, ginxsom, fips, nginx) and logs the parse error to stderr (captured by journald, per the service unit's `StandardError=journal`).
### 4.2 Install location
Shipped in the chroot at [`iso/config/includes.chroot/etc/n-os-tr/services.conf`](../iso/config/includes.chroot/etc/n-os-tr/services.conf). Mode `0644`, owner `root:root`.
## 5. Control path: talking to systemd
### 5.1 Transport choice
The TUI needs to:
1. Query each unit's current state (`LoadState`, `ActiveState`, `SubState`).
2. Start and stop units.
3. Receive job-completion signals.
Two realistic options in a static musl C binary with no glibc:
| Option | Notes | Verdict |
|----------------------------------------------------------------|---------------------------------------------------------------------------------------------|---------------------|
| **Shell out to `systemctl`** via `fork` + `execve` | Simple. Exit codes are well-defined. Parse `show --property=ActiveState=`. No linking. | **v1 default.** |
| **Speak the D-Bus wire protocol directly** to `/run/dbus/system_bus_socket` | Avoids forking. Enables real-time `JobRemoved` signals. Needs a tiny D-Bus marshaller (~1 KLOC) or the vendored cjson-style lib. | Later optimization. |
| **Link `libsystemd.a`** | Would pull in a chunk of systemd source into Alpine build, and libsystemd isn't available as `-static` in Alpine. | Rejected. |
v1 uses `systemctl`. The binary path is hardcoded to `/bin/systemctl` (present on every Debian live image — `systemctl` itself is part of `systemd` which is in base). The wrapper module is `stack/nostr-id-tui/src/systemd_client.c` and exposes:
```c
typedef enum {
SVC_INACTIVE = 0,
SVC_ACTIVATING,
SVC_ACTIVE,
SVC_DEACTIVATING,
SVC_FAILED,
SVC_UNKNOWN
} svc_state_t;
svc_state_t systemd_active_state(const char *unit); /* systemctl show -p ActiveState */
int systemd_start(const char *unit, int timeout_ms, char *err_out, size_t err_len);
int systemd_stop (const char *unit, int timeout_ms, char *err_out, size_t err_len);
int systemd_journal_tail(const char *unit, int n_lines, FILE *out); /* exec journalctl */
```
Each call spawns `systemctl` / `journalctl` with `posix_spawn` into a pipe, waits with a timeout, captures stdout/stderr, and returns.
### 5.2 Privilege
`nostr-id-tui.service` currently runs as root on `tty1` (see the service unit). That gives it direct `systemctl` privilege — no polkit rule needed. This matches the existing threat-model stance in [`plans/tui_login.md §6.1`](tui_login.md#61-etcsystemdsystemnostr-id-tuiservice): the TUI is already privileged because it owns the tty and talks to identity-agent over a root-only socket.
If we ever demote the TUI to run as the `nostr-id` user, we'll need `/etc/polkit-1/rules.d/50-n-os-tr-services.rules` granting that user the `org.freedesktop.systemd1.manage-units` action **only for the whitelisted units listed in `services.conf`**. That's future work.
### 5.3 Timeouts and cancellation
- Per-unit start/stop timeout: 30 s (covers nginx startup, fips mesh ready, etc.). Larger than any real service needs.
- TUI-level overall budget: 2 minutes. If exceeded, Apply falls through to ApplyError with any unfinished jobs marked `[TIME]` and the user is prompted.
- Ctrl-C in the Apply screen sends SIGINT to the current `systemctl` child, marks the remaining jobs `[SKIP ]`, and goes to ApplyError.
## 6. Source tree changes
Extending the layout declared in [`plans/tui_login.md §2.2`](tui_login.md#22-code-that-lives-in-this-repo-n_os_tr):
```
stack/nostr-id-tui/
├── src/
│ ├── main.c # now dispatches identity→services→exit
│ ├── screen_menu.c # unchanged
│ ├── screen_enter.c # unchanged
│ ├── screen_generate.c # unchanged
│ ├── screen_import.c # unchanged
│ ├── screen_bunker.c # unchanged
│ ├── screen_services.c # NEW: main service toggle screen
│ ├── screen_services_confirm.c # NEW: confirm + apply screens
│ ├── services_conf.c # NEW: parser for /etc/n-os-tr/services.conf
│ ├── systemd_client.c # NEW: fork/exec systemctl + journalctl
│ ├── curses_helpers.c # unchanged
│ ├── agent_client.c # unchanged
│ └── memhygiene.c # unchanged
├── include/
│ └── n_os_tr_tui.h # adds svc_* decls + ui_backend hooks
└── tests/
├── test_screen_services.c # NEW: state-machine unit test
├── test_services_conf.c # NEW: parser fuzz/table tests
├── test_systemd_client.c # NEW: runs against a fake systemctl shim
└── test_integration.sh # extended to toggle a service in QEMU
```
No ncurses API leaks into the new state machines. All rendering goes through the same `ui_backend_t` abstraction established in [`plans/tui_login.md §7`](tui_login.md#7-per-flavor-abstraction). The Pi LCD port will get a stripped service menu for free.
## 7. Systemd preset policy
### 7.1 What "installed but not enabled" means
For each toggleable unit we ship:
- The unit file in `iso/config/includes.chroot/etc/systemd/system/<unit>` or (for distro-packaged units like `ssh`, `tor`, `nginx`) in the package itself.
- **No symlink in `multi-user.target.wants/`** — i.e. no `systemctl enable`.
- A preset file that explicitly forbids enabling at image-build time.
### 7.2 Preset file
New file: [`iso/config/includes.chroot/etc/systemd/system-preset/50-n-os-tr.preset`](../iso/config/includes.chroot/etc/systemd/system-preset/50-n-os-tr.preset):
```
# n-OS-tr service posture: only the identity/boot pipeline auto-starts.
# Everything else is explicitly disabled. The TUI turns them on at the
# user's request after identity load.
enable nostr-id.service
enable nostr-id-tui.service
enable n-os-tr-firstboot.service
disable c-relay.service
disable ginxsom.service
disable fips.service
disable nginx.service
disable nostr-config-loader.service
disable nostr-config-writer.service
disable ssh.service
disable tor.service
```
The `50-` prefix lands before Debian defaults (typically `90-*.preset`), so our `disable` lines override e.g. nginx's upstream preset-enable.
### 7.3 Rework of the enable-services hook
Current hook [`iso/config/hooks/live/0020-enable-services.hook.chroot`](../iso/config/hooks/live/0020-enable-services.hook.chroot) enables the services directly. Rework it to be explicit about the new posture:
```sh
#!/bin/sh
set -eu
# Enable the always-on trio
systemctl enable n-os-tr-firstboot.service
systemctl enable nostr-id.service
systemctl enable nostr-id-tui.service
# Explicitly disable everything else in the service manifest.
# Idempotent; safe to re-run. `systemctl disable` is a no-op for units
# that aren't enabled yet, which is what we want.
for unit in \
c-relay.service \
ginxsom.service \
fips.service \
nginx.service \
nostr-config-loader.service \
nostr-config-writer.service \
ssh.service \
tor.service ; do
systemctl disable "$unit" 2>/dev/null || true
done
# Run the preset policy for anything we didn't list above (defensive).
systemctl preset-all --preset-mode=enable-only 2>/dev/null || true
```
### 7.4 Package-list additions
`tor` is new to the image. Add to [`iso/config/package-lists/base.list.chroot`](../iso/config/package-lists/base.list.chroot):
```
tor
```
No other new packages: nginx, openssh-server, ca-certificates, systemd, etc. are already pulled.
## 8. Interaction with identity modes
### 8.1 Normal, Bunker, and Amnesia modes
- **Normal (mnemonic loaded)**: full menu, full defaults as in §1.1.
- **Bunker mode**: services that require a raw local nsec are **disabled in the menu** (greyed out with `[ n/a ]` in the state column). Those are:
- `c-relay` — admin key comes from identity-agent via NIP-46 signing for event-based config only; for the admin nsec path the service fails. The menu shows `[ n/a ]` with a tooltip line "requires local key; bunker mode cannot provide".
- `ginxsom` — same. Key material is needed at process start (`--server-privkey` in [`ginxsom.service`](../iso/config/includes.chroot/etc/systemd/system/ginxsom.service)).
- `fips` — same.
- `nginx`, `config-loader`, `config-writer`, `ssh`, `tor` — available in bunker mode.
- **Amnesia mode**: all services available, but `config-loader`/`config-writer` are explicitly disabled with label "amnesia session — config not persisted" (we don't pull anything from Nostr for an ephemeral identity).
The rules live in `services_conf.c` as a small matrix; the file format in §4.1 can be extended later with `modes=normal,bunker,amnesia` but v1 hardcodes the matrix in C.
### 8.2 Re-entry
A future option from the Services menu: `Q) Reset identity and return to identity menu`. Deferred — not in v1. For v1 the only path out of Services is `C` (commit) or `X` (skip), both of which lead to Exit.
## 9. Testing
### 9.1 Unit (in Alpine/musl builder)
- `test_services_conf` — feeds valid and malformed manifests, asserts parser produces correct structs and correct fallback on bad input.
- `test_systemd_client` — sets `$PATH` to a directory with a shim `systemctl` that records its argv and emits canned output; asserts our client parses `ActiveState=active|inactive|failed` correctly and times out when the shim sleeps.
- `test_screen_services` — drives the services state machine through a mock `ui_backend_t`; asserts:
- Default state is all-off.
- Toggling `R` then `G` then `R` ends with `G` on and `R` off.
- Toggling `R` prompts for `N` enable; answering `y` enables both.
- `Z` (reset) returns to defaults.
- `X` (skip) goes directly to Apply with an empty plan.
- `C` (commit) goes to Confirm.
### 9.2 Integration (QEMU)
Extend [`stack/nostr-id-tui/tests/test_integration.sh`](../stack/nostr-id-tui/tests/test_integration.sh) with a new scripted run:
1. Boot the ISO.
2. At the identity screen, feed the Trezor test-vector mnemonic.
3. At the services screen, press `N` (enable nginx), then `C`, then `Y`.
4. After the TUI exits and getty appears, log in as the default user.
5. Assert `systemctl is-active nginx` = `active` and `systemctl is-active c-relay` = `inactive`.
### 9.3 Smoketest extensions
Extend [`iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) to also verify:
- Preset posture at **image build time**: for each toggleable unit, `systemctl is-enabled <unit>` returns `disabled`.
- The manifest file `/etc/n-os-tr/services.conf` exists and parses (re-invoke the parser from a test helper).
- `/bin/systemctl` is present (the TUI depends on it).
## 10. Non-persistence and the future Nostr slot
v1: selections are session-local. Reboot → all services back to off, user re-selects.
Future: one new entry in the manifest scheme from [`plans/nostr_config_projection.md §2.1`](nostr_config_projection.md#21-slots):
```json
{
"name": "n-os-tr/services",
"path": "/run/nostr-id/services.state.json",
"mode": "0600",
"encrypted": true
}
```
Content schema (draft):
```json
{
"version": 1,
"enabled": ["nginx.service", "c-relay.service", "fips.service"]
}
```
When a session starts, if a services slot exists, the TUI pre-loads the toggle state from it. User still has to press `C` to commit — we don't auto-apply without the user seeing the menu. This preserves the "the user decides when to turn them on" rule.
Out of scope for v1 implementation.
## 11. Failure modes
| Failure | Behavior |
|-------------------------------------------------|--------------------------------------------------------------------------|
| `/etc/n-os-tr/services.conf` missing or unparseable | Log to stderr, fall back to hardcoded minimal list (c-relay, ginxsom, fips, nginx). |
| `/bin/systemctl` missing | FatalError (as in [`plans/tui_login.md §9`](tui_login.md#9-failure-modes-summary)). The ISO is malformed. |
| Unit start returns non-zero | Row shows `[FAIL ]`, ApplyError screen is reached after all jobs settle. |
| Unit start hangs past 30 s | Timeout, row shows `[TIME]`, ApplyError. |
| User hits Ctrl-C during Apply | SIGINT to current `systemctl`, remaining jobs `[SKIP ]`, ApplyError. |
| Bunker mode, user tries to toggle `c-relay` | Toggle is ignored; status line shows "requires local key; bunker mode". |
| User toggles all off and presses Commit | Apply plan is empty; TUI prints `No services requested. Handing control to login...` and exits. |
| Identity agent dies during service menu | We're not calling the agent during this phase — selections are in TUI memory only. User is unaffected until they finish service menu and the TUI exits; if the agent is dead when services start, those services will fail on their own dependency chain. |
## 12. Docs to update
- [`plans/tui_login.md`](tui_login.md) — add a one-line reference in §3.1 to the new `Services` state after `SendAgent`; add `screen_services.c`, `screen_services_confirm.c`, `services_conf.c`, `systemd_client.c` to the source tree listing in §2.2; update §13 implementation order with the service-menu steps.
- [`plans/iso_architecture.md`](iso_architecture.md) — update §4 boot sequence to show the identity+services TUI phase before the parallel `fips/c-relay/ginxsom/nginx` block, and note that those services are started by the TUI, not auto-started by systemd.
- [`iso/config/includes.chroot/usr/share/doc/n-os-tr/README`](../iso/config/includes.chroot/usr/share/doc/n-os-tr/README) — describe the new login→services flow for end users.
- [`docs/RUNNING.md`](../docs/RUNNING.md) — add a section showing the service menu and how to toggle.
## 13. Implementation order (Phase 3a.2)
Small, independently verifiable steps. Each one leaves the repo in a runnable state.
1. **Land the preset file and rework the enable hook.** After this commit, a fresh ISO boots to a system where only `nostr-id`, `nostr-id-tui`, and `firstboot` are enabled; every other service is present on disk but inactive. Verify with `systemctl list-unit-files --state=disabled` in a QEMU boot.
2. **Add the service manifest file.** Ship `/etc/n-os-tr/services.conf` with the v1 table from §1.1. No TUI change yet; verifies that the file reaches the image.
3. **Add `tor` to the base package list.** Confirm the built ISO includes `tor.service` as a disabled unit.
4. **Implement `services_conf.c` + tests.** Parser plus a 40-line sanity test in the Alpine builder.
5. **Implement `systemd_client.c` + tests.** Fork/exec + pipe capture + shim-based test.
6. **Implement `screen_services.c` state machine + `test_screen_services`.** No rendering yet — verifies transitions against a mock backend.
7. **Render the services screen in `ui_backend_curses.c`.** First visible new screen.
8. **Implement `screen_services_confirm.c` — confirm/apply/apply-error screens.** Wire them to `systemd_client`.
9. **Hook into `main.c`.** After `agent_client` returns from `load_mnemonic`/`load_bunker`/`load_amnesia` with ok, transition to the services screen. Skip the screen entirely if `/etc/n-os-tr/services.conf` is missing *and* fallback is disabled via a `--no-services` flag (useful for tests that want only the identity flow).
10. **Extend the QEMU integration test.** Script a toggle of one service, assert post-login that the chosen service is active and the others are not.
11. **Extend the smoketest.** Add the preset-state check.
12. **Doc passes.** Update [`plans/tui_login.md`](tui_login.md), [`plans/iso_architecture.md`](iso_architecture.md), [`docs/RUNNING.md`](../docs/RUNNING.md), and the in-ISO README.
## 14. Open questions
- **`tor.service` posture out of the box.** We ship the distro default `torrc` (SOCKS 9050 only, no relay). Do we want a second unit drop-in that enables the hidden-service onion for nginx when the user turns both on? Probably yes, as a follow-up; keep v1 minimal.
- **GUI flavor.** Image flavor with a GUI will run the TUI on a text VT during early boot. Does it also allow the user to re-run the services menu later from the GUI? Probably yes via a GTK wrapper around the same `systemd_client` module, but that's post-v1.
- **Per-user vs system.** All services here are system-level. "Which user's `ssh/authorized_keys` does `ssh` serve" is answered by the config-loader slot scheme. No per-user service toggles in v1.
- **Should toggling the menu's state affect *default* behavior for future reboots?** Today: no; v1 is stateless. A future Nostr slot (§10) could record the last committed state and let the TUI show it as a "remembered" default, but that's explicit future work.

View File

@@ -18,6 +18,8 @@ endif()
add_executable(nostr-id-tui
src/main.c
src/services_conf.c
src/systemd_client.c
)
target_include_directories(nostr-id-tui PRIVATE

View File

@@ -85,12 +85,12 @@ clean: CMakeFiles/nostr-id-tui.dir/clean
CMakeFiles/nostr-id-tui.dir/all:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/depend
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/build
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=1,2 "Built target nostr-id-tui"
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=1,2,3,4 "Built target nostr-id-tui"
.PHONY : CMakeFiles/nostr-id-tui.dir/all
# Build rule for subdir invocation for target.
CMakeFiles/nostr-id-tui.dir/rule: cmake_check_build_system
$(CMAKE_COMMAND) -E cmake_progress_start /home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles 2
$(CMAKE_COMMAND) -E cmake_progress_start /home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles 4
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 CMakeFiles/nostr-id-tui.dir/all
$(CMAKE_COMMAND) -E cmake_progress_start /home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles 0
.PHONY : CMakeFiles/nostr-id-tui.dir/rule
@@ -102,7 +102,7 @@ nostr-id-tui: CMakeFiles/nostr-id-tui.dir/rule
# codegen rule for target.
CMakeFiles/nostr-id-tui.dir/codegen:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/codegen
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=1,2 "Finished codegen for target nostr-id-tui"
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=1,2,3,4 "Finished codegen for target nostr-id-tui"
.PHONY : CMakeFiles/nostr-id-tui.dir/codegen
# clean rule for target.

View File

@@ -9,6 +9,8 @@ set(CMAKE_DEPENDS_LANGUAGES
# The set of dependency files which are needed:
set(CMAKE_DEPENDS_DEPENDENCY_FILES
"/home/user/lt/n_os_tr/stack/nostr-id-tui/src/main.c" "CMakeFiles/nostr-id-tui.dir/src/main.c.o" "gcc" "CMakeFiles/nostr-id-tui.dir/src/main.c.o.d"
"/home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c" "CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o" "gcc" "CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o.d"
"/home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c" "CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o" "gcc" "CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o.d"
"" "nostr-id-tui" "gcc" "CMakeFiles/nostr-id-tui.dir/link.d"
)

View File

@@ -86,21 +86,53 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.s: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/nostr-id-tui.dir/src/main.c.s"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/user/lt/n_os_tr/stack/nostr-id-tui/src/main.c -o CMakeFiles/nostr-id-tui.dir/src/main.c.s
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o: CMakeFiles/nostr-id-tui.dir/flags.make
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o: /home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o: CMakeFiles/nostr-id-tui.dir/compiler_depend.ts
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building C object CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o -MF CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o.d -o CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o -c /home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.i: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/nostr-id-tui.dir/src/services_conf.c.i"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c > CMakeFiles/nostr-id-tui.dir/src/services_conf.c.i
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.s: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/nostr-id-tui.dir/src/services_conf.c.s"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c -o CMakeFiles/nostr-id-tui.dir/src/services_conf.c.s
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o: CMakeFiles/nostr-id-tui.dir/flags.make
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o: /home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o: CMakeFiles/nostr-id-tui.dir/compiler_depend.ts
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building C object CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o -MF CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o.d -o CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o -c /home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.i: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.i"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c > CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.i
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.s: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.s"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c -o CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.s
# Object files for target nostr-id-tui
nostr__id__tui_OBJECTS = \
"CMakeFiles/nostr-id-tui.dir/src/main.c.o"
"CMakeFiles/nostr-id-tui.dir/src/main.c.o" \
"CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o" \
"CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o"
# External object files for target nostr-id-tui
nostr__id__tui_EXTERNAL_OBJECTS =
nostr-id-tui: CMakeFiles/nostr-id-tui.dir/src/main.c.o
nostr-id-tui: CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o
nostr-id-tui: CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o
nostr-id-tui: CMakeFiles/nostr-id-tui.dir/build.make
nostr-id-tui: CMakeFiles/nostr-id-tui.dir/compiler_depend.ts
nostr-id-tui: /home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/libnostr_core_x64.a
nostr-id-tui: /usr/lib/x86_64-linux-gnu/libcurses.so
nostr-id-tui: /usr/lib/x86_64-linux-gnu/libform.so
nostr-id-tui: CMakeFiles/nostr-id-tui.dir/link.txt
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --bold --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking C executable nostr-id-tui"
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --bold --progress-dir=/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Linking C executable nostr-id-tui"
$(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/nostr-id-tui.dir/link.txt --verbose=$(VERBOSE)
# Rule to build all files generated by this target.

View File

@@ -2,6 +2,10 @@ file(REMOVE_RECURSE
"CMakeFiles/nostr-id-tui.dir/link.d"
"CMakeFiles/nostr-id-tui.dir/src/main.c.o"
"CMakeFiles/nostr-id-tui.dir/src/main.c.o.d"
"CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o"
"CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o.d"
"CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o"
"CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o.d"
"nostr-id-tui"
"nostr-id-tui.pdb"
)

View File

@@ -52,12 +52,11 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o
/usr/include/x86_64-linux-gnu/bits/floatn.h
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h
/usr/include/x86_64-linux-gnu/bits/long-double.h
/usr/include/x86_64-linux-gnu/bits/select.h
/usr/include/x86_64-linux-gnu/bits/stdint-intn.h
/usr/include/x86_64-linux-gnu/bits/stdint-least.h
/usr/include/x86_64-linux-gnu/bits/stdint-uintn.h
/usr/include/x86_64-linux-gnu/bits/stdio.h
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h
/usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h
/usr/include/x86_64-linux-gnu/bits/time.h
/usr/include/x86_64-linux-gnu/bits/time64.h
@@ -68,8 +67,11 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h
/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h
/usr/include/x86_64-linux-gnu/bits/types/clock_t.h
/usr/include/x86_64-linux-gnu/bits/types/sigset_t.h
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h
/usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h
/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h
/usr/include/x86_64-linux-gnu/bits/types/time_t.h
/usr/include/x86_64-linux-gnu/bits/typesizes.h
@@ -78,11 +80,93 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
/usr/include/x86_64-linux-gnu/gnu/stubs.h
/usr/include/x86_64-linux-gnu/sys/cdefs.h
/usr/include/x86_64-linux-gnu/sys/select.h
/usr/include/x86_64-linux-gnu/sys/time.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdbool.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdint.h
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h
/usr/include/ctype.h
/usr/include/features-time64.h
/usr/include/features.h
/usr/include/stdc-predef.h
/usr/include/stdio.h
/usr/include/stdlib.h
/usr/include/string.h
/usr/include/x86_64-linux-gnu/bits/endian.h
/usr/include/x86_64-linux-gnu/bits/endianness.h
/usr/include/x86_64-linux-gnu/bits/floatn-common.h
/usr/include/x86_64-linux-gnu/bits/floatn.h
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h
/usr/include/x86_64-linux-gnu/bits/long-double.h
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h
/usr/include/x86_64-linux-gnu/bits/time64.h
/usr/include/x86_64-linux-gnu/bits/timesize.h
/usr/include/x86_64-linux-gnu/bits/types.h
/usr/include/x86_64-linux-gnu/bits/types/FILE.h
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h
/usr/include/x86_64-linux-gnu/bits/typesizes.h
/usr/include/x86_64-linux-gnu/bits/wordsize.h
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
/usr/include/x86_64-linux-gnu/gnu/stubs.h
/usr/include/x86_64-linux-gnu/sys/cdefs.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h
/usr/include/asm-generic/errno-base.h
/usr/include/asm-generic/errno.h
/usr/include/errno.h
/usr/include/features-time64.h
/usr/include/features.h
/usr/include/linux/errno.h
/usr/include/stdc-predef.h
/usr/include/stdio.h
/usr/include/stdlib.h
/usr/include/string.h
/usr/include/unistd.h
/usr/include/x86_64-linux-gnu/bits/confname.h
/usr/include/x86_64-linux-gnu/bits/errno.h
/usr/include/x86_64-linux-gnu/bits/floatn-common.h
/usr/include/x86_64-linux-gnu/bits/floatn.h
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h
/usr/include/x86_64-linux-gnu/bits/long-double.h
/usr/include/x86_64-linux-gnu/bits/posix_opt.h
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h
/usr/include/x86_64-linux-gnu/bits/time64.h
/usr/include/x86_64-linux-gnu/bits/timesize.h
/usr/include/x86_64-linux-gnu/bits/types.h
/usr/include/x86_64-linux-gnu/bits/types/FILE.h
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h
/usr/include/x86_64-linux-gnu/bits/typesizes.h
/usr/include/x86_64-linux-gnu/bits/unistd_ext.h
/usr/include/x86_64-linux-gnu/bits/waitflags.h
/usr/include/x86_64-linux-gnu/bits/waitstatus.h
/usr/include/x86_64-linux-gnu/bits/wordsize.h
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
/usr/include/x86_64-linux-gnu/gnu/stubs.h
/usr/include/x86_64-linux-gnu/sys/cdefs.h
/usr/include/x86_64-linux-gnu/sys/wait.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h
/usr/lib/linux/uapi/x86/asm/errno.h
nostr-id-tui
/home/user/lt/n_os_tr/includes/nostr_core_lib/libnostr_core_x64.a
/lib/x86_64-linux-gnu/libc.so.6
@@ -112,4 +196,6 @@ nostr-id-tui
/usr/lib/x86_64-linux-gnu/libc_nonshared.a
/usr/lib/x86_64-linux-gnu/libform.so
/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/nostr-id-tui.dir/src/main.c.o
/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o
/home/user/lt/n_os_tr/stack/nostr-id-tui/build-host/CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o

View File

@@ -51,12 +51,11 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o: /home/user/lt/n_os_tr/stack/nostr-id-t
/usr/include/x86_64-linux-gnu/bits/floatn.h \
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
/usr/include/x86_64-linux-gnu/bits/long-double.h \
/usr/include/x86_64-linux-gnu/bits/select.h \
/usr/include/x86_64-linux-gnu/bits/stdint-intn.h \
/usr/include/x86_64-linux-gnu/bits/stdint-least.h \
/usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \
/usr/include/x86_64-linux-gnu/bits/stdio.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h \
/usr/include/x86_64-linux-gnu/bits/time.h \
/usr/include/x86_64-linux-gnu/bits/time64.h \
@@ -67,8 +66,11 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o: /home/user/lt/n_os_tr/stack/nostr-id-t
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \
/usr/include/x86_64-linux-gnu/bits/types/clock_t.h \
/usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \
/usr/include/x86_64-linux-gnu/bits/types/time_t.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h \
@@ -77,11 +79,91 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o: /home/user/lt/n_os_tr/stack/nostr-id-t
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/sys/select.h \
/usr/include/x86_64-linux-gnu/sys/time.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdbool.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdint.h
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o: /home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c \
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h \
/usr/include/ctype.h \
/usr/include/features-time64.h \
/usr/include/features.h \
/usr/include/stdc-predef.h \
/usr/include/stdio.h \
/usr/include/stdlib.h \
/usr/include/string.h \
/usr/include/x86_64-linux-gnu/bits/endian.h \
/usr/include/x86_64-linux-gnu/bits/endianness.h \
/usr/include/x86_64-linux-gnu/bits/floatn-common.h \
/usr/include/x86_64-linux-gnu/bits/floatn.h \
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
/usr/include/x86_64-linux-gnu/bits/long-double.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h \
/usr/include/x86_64-linux-gnu/bits/time64.h \
/usr/include/x86_64-linux-gnu/bits/timesize.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/types/FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o: /home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c \
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h \
/usr/include/asm-generic/errno-base.h \
/usr/include/asm-generic/errno.h \
/usr/include/errno.h \
/usr/include/features-time64.h \
/usr/include/features.h \
/usr/include/linux/errno.h \
/usr/include/stdc-predef.h \
/usr/include/stdio.h \
/usr/include/stdlib.h \
/usr/include/string.h \
/usr/include/unistd.h \
/usr/include/x86_64-linux-gnu/bits/confname.h \
/usr/include/x86_64-linux-gnu/bits/errno.h \
/usr/include/x86_64-linux-gnu/bits/floatn-common.h \
/usr/include/x86_64-linux-gnu/bits/floatn.h \
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
/usr/include/x86_64-linux-gnu/bits/long-double.h \
/usr/include/x86_64-linux-gnu/bits/posix_opt.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h \
/usr/include/x86_64-linux-gnu/bits/time64.h \
/usr/include/x86_64-linux-gnu/bits/timesize.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/types/FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h \
/usr/include/x86_64-linux-gnu/bits/unistd_ext.h \
/usr/include/x86_64-linux-gnu/bits/waitflags.h \
/usr/include/x86_64-linux-gnu/bits/waitstatus.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/sys/wait.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h \
/usr/lib/linux/uapi/x86/asm/errno.h
nostr-id-tui: /home/user/lt/n_os_tr/includes/nostr_core_lib/libnostr_core_x64.a \
/lib/x86_64-linux-gnu/libc.so.6 \
/lib/x86_64-linux-gnu/libm.so.6 \
@@ -109,219 +191,263 @@ nostr-id-tui: /home/user/lt/n_os_tr/includes/nostr_core_lib/libnostr_core_x64.a
/usr/lib/gcc/x86_64-linux-gnu/14/libgcc_s.so \
/usr/lib/x86_64-linux-gnu/libc_nonshared.a \
/usr/lib/x86_64-linux-gnu/libform.so \
CMakeFiles/nostr-id-tui.dir/src/main.c.o
CMakeFiles/nostr-id-tui.dir/src/main.c.o \
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o \
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o
/usr/lib/x86_64-linux-gnu/libform.so:
/usr/lib/x86_64-linux-gnu/libc_nonshared.a:
/usr/lib/gcc/x86_64-linux-gnu/14/libgcc.a:
/usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o:
/usr/lib/x86_64-linux-gnu/libz.so:
/usr/lib/x86_64-linux-gnu/libtinfo.so:
/usr/lib/x86_64-linux-gnu/libssl.so:
/usr/lib/x86_64-linux-gnu/libpthread.a:
/usr/lib/x86_64-linux-gnu/libncurses.so.6:
/usr/lib/x86_64-linux-gnu/libdl.a:
/usr/lib/x86_64-linux-gnu/libcrypto.so:
/usr/lib/x86_64-linux-gnu/libc.so:
/usr/lib/x86_64-linux-gnu/crti.o:
/usr/lib/x86_64-linux-gnu/Scrt1.o:
/lib/x86_64-linux-gnu/libmvec.so.1:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdint.h:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdbool.h:
/usr/include/x86_64-linux-gnu/sys/cdefs.h:
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h:
/usr/include/x86_64-linux-gnu/bits/wordsize.h:
/usr/include/x86_64-linux-gnu/bits/typesizes.h:
CMakeFiles/nostr-id-tui.dir/src/main.c.o:
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h:
/usr/lib/x86_64-linux-gnu/libm.so:
/lib/x86_64-linux-gnu/libc.so.6:
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h:
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h:
/usr/include/x86_64-linux-gnu/bits/types.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_secure_memory.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_fips_ipv6.h:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h:
/usr/include/ctype.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_bip39_ui.h:
/usr/include/curses.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip060.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/cjson/cJSON.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip021.h:
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip013.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/request_validator.h:
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip004.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_core.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip061.h:
/usr/include/x86_64-linux-gnu/bits/stdint-least.h:
/usr/lib/x86_64-linux-gnu/libcurses.so:
/usr/include/x86_64-linux-gnu/bits/time.h:
/usr/include/x86_64-linux-gnu/gnu/stubs.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/blossom_client.h:
/usr/lib/x86_64-linux-gnu/libsecp256k1.so:
/usr/include/x86_64-linux-gnu/bits/floatn-common.h:
/usr/include/x86_64-linux-gnu/bits/wchar.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_common.h:
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/main.c:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_http.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip046.h:
/lib/x86_64-linux-gnu/libm.so.6:
/usr/include/x86_64-linux-gnu/bits/types/time_t.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip006.h:
/usr/include/x86_64-linux-gnu/bits/types/clock_t.h:
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/cashu_mint.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip011.h:
/lib64/ld-linux-x86-64.so.2:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip042.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip005.h:
/usr/include/x86_64-linux-gnu/bits/long-double.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip019.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip003.h:
/usr/include/x86_64-linux-gnu/bits/floatn.h:
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h:
/usr/lib/x86_64-linux-gnu/crtn.o:
/usr/include/unctrl.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/utils.h:
/usr/include/x86_64-linux-gnu/bits/time64.h:
/usr/include/features-time64.h:
/usr/include/features.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip017.h:
/usr/include/ncurses_dll.h:
/usr/lib/gcc/x86_64-linux-gnu/14/libgcc_s.so:
/usr/include/stdint.h:
/usr/include/x86_64-linux-gnu/bits/timesize.h:
/usr/include/stdio.h:
/usr/include/stdlib.h:
/usr/lib/x86_64-linux-gnu/libgcc_s.so.1:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_log.h:
/usr/include/x86_64-linux-gnu/bits/endian.h:
/usr/include/string.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/libnostr_core_x64.a:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip044.h:
/usr/include/x86_64-linux-gnu/bits/stdint-uintn.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip001.h:
/usr/include/time.h:
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h:
/usr/include/sqlite3.h:
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o:
/usr/include/x86_64-linux-gnu/bits/endianness.h:
/usr/include/sqlite3.h:
/usr/include/x86_64-linux-gnu/bits/stdint-intn.h:
/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h:
/usr/include/x86_64-linux-gnu/bits/types.h:
/usr/include/x86_64-linux-gnu/bits/types/FILE.h:
/usr/include/time.h:
/usr/include/x86_64-linux-gnu/bits/stdio.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip001.h:
/usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h:
/usr/lib/x86_64-linux-gnu/libcurl.so:
/home/user/lt/n_os_tr/includes/nostr_core_lib/libnostr_core_x64.a:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip059.h:
/usr/include/string.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_log.h:
/usr/lib/x86_64-linux-gnu/libgcc_s.so.1:
/usr/include/x86_64-linux-gnu/bits/timesize.h:
/usr/include/stdint.h:
/usr/lib/gcc/x86_64-linux-gnu/14/libgcc_s.so:
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h:
/usr/include/stdc-predef.h:
/usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip059.h:
/usr/lib/x86_64-linux-gnu/libcurl.so:
/usr/include/ncurses_dll.h:
/usr/include/features.h:
/usr/include/features-time64.h:
/usr/include/x86_64-linux-gnu/bits/select.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/utils.h:
/usr/include/x86_64-linux-gnu/bits/types/sigset_t.h:
/usr/include/unctrl.h:
/usr/lib/x86_64-linux-gnu/crtn.o:
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip017.h:
/usr/include/x86_64-linux-gnu/bits/floatn.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip003.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip019.h:
/usr/include/x86_64-linux-gnu/bits/long-double.h:
/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip005.h:
/usr/include/x86_64-linux-gnu/bits/errno.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip011.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/cashu_mint.h:
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h:
/usr/include/x86_64-linux-gnu/bits/types/clock_t.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip006.h:
/lib64/ld-linux-x86-64.so.2:
/usr/include/x86_64-linux-gnu/bits/types/time_t.h:
/lib/x86_64-linux-gnu/libm.so.6:
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/main.c:
/usr/include/x86_64-linux-gnu/bits/wchar.h:
/usr/include/x86_64-linux-gnu/bits/floatn-common.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_http.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/blossom_client.h:
/usr/include/x86_64-linux-gnu/bits/endian.h:
/usr/include/x86_64-linux-gnu/gnu/stubs.h:
/usr/include/x86_64-linux-gnu/bits/time.h:
/usr/lib/x86_64-linux-gnu/libcurses.so:
/usr/include/x86_64-linux-gnu/bits/stdint-least.h:
/usr/include/x86_64-linux-gnu/bits/time64.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip004.h:
/usr/include/stdio.h:
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c:
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/request_validator.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip013.h:
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip021.h:
/usr/include/x86_64-linux-gnu/bits/waitstatus.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/cjson/cJSON.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip060.h:
/usr/include/curses.h:
/usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_bip39_ui.h:
/usr/include/ctype.h:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h:
/usr/include/stdlib.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_fips_ipv6.h:
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip044.h:
/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h:
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h:
/lib/x86_64-linux-gnu/libc.so.6:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_common.h:
/usr/lib/x86_64-linux-gnu/libm.so:
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h:
/usr/include/x86_64-linux-gnu/sys/cdefs.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip046.h:
/usr/include/x86_64-linux-gnu/bits/typesizes.h:
/usr/include/x86_64-linux-gnu/bits/wordsize.h:
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h:
/usr/include/x86_64-linux-gnu/sys/time.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip042.h:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdbool.h:
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h:
/usr/include/x86_64-linux-gnu/sys/wait.h:
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdint.h:
/usr/include/asm-generic/errno-base.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nip061.h:
/usr/lib/x86_64-linux-gnu/libncurses.so.6:
/usr/lib/x86_64-linux-gnu/libsecp256k1.so:
/usr/include/asm-generic/errno.h:
/usr/include/linux/errno.h:
/usr/include/x86_64-linux-gnu/bits/stdint-uintn.h:
/usr/include/x86_64-linux-gnu/sys/select.h:
/usr/include/unistd.h:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_secure_memory.h:
/usr/include/x86_64-linux-gnu/bits/confname.h:
/usr/include/x86_64-linux-gnu/bits/posix_opt.h:
/usr/include/x86_64-linux-gnu/bits/waitflags.h:
/usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h:
/usr/lib/linux/uapi/x86/asm/errno.h:
/lib/x86_64-linux-gnu/libmvec.so.1:
/usr/lib/x86_64-linux-gnu/Scrt1.o:
/usr/lib/x86_64-linux-gnu/libc.so:
CMakeFiles/nostr-id-tui.dir/src/main.c.o:
/usr/lib/x86_64-linux-gnu/libcrypto.so:
/usr/lib/x86_64-linux-gnu/libdl.a:
/usr/lib/x86_64-linux-gnu/libpthread.a:
/usr/lib/x86_64-linux-gnu/libssl.so:
/home/user/lt/n_os_tr/includes/nostr_core_lib/nostr_core/nostr_core.h:
/usr/include/errno.h:
/usr/lib/x86_64-linux-gnu/libtinfo.so:
/usr/lib/x86_64-linux-gnu/crti.o:
/usr/lib/x86_64-linux-gnu/libz.so:
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h:
/usr/include/x86_64-linux-gnu/bits/types/FILE.h:
/usr/lib/gcc/x86_64-linux-gnu/14/crtendS.o:
/usr/lib/gcc/x86_64-linux-gnu/14/libgcc.a:
/usr/include/x86_64-linux-gnu/bits/unistd_ext.h:
/usr/lib/x86_64-linux-gnu/libc_nonshared.a:
/usr/lib/x86_64-linux-gnu/libform.so:
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o:

View File

@@ -3,6 +3,8 @@ nostr-id-tui: \
/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/crti.o \
/usr/lib/gcc/x86_64-linux-gnu/14/crtbeginS.o \
CMakeFiles/nostr-id-tui.dir/src/main.c.o \
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o \
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o \
/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/libnostr_core_x64.a \
/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/libcurses.so \
/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/libcurses.so \
@@ -52,6 +54,10 @@ nostr-id-tui: \
CMakeFiles/nostr-id-tui.dir/src/main.c.o:
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o:
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o:
/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/libnostr_core_x64.a:
/usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/libcurses.so:

View File

@@ -1 +1 @@
/usr/bin/cc -g -Wl,--dependency-file=CMakeFiles/nostr-id-tui.dir/link.d "CMakeFiles/nostr-id-tui.dir/src/main.c.o" -o nostr-id-tui /home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/libnostr_core_x64.a -lcurses /usr/lib/x86_64-linux-gnu/libform.so -lsecp256k1 -lssl -lcrypto -lcurl -lz -lpthread -lm -ldl
/usr/bin/cc -g -Wl,--dependency-file=CMakeFiles/nostr-id-tui.dir/link.d "CMakeFiles/nostr-id-tui.dir/src/main.c.o" "CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o" "CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o" -o nostr-id-tui /home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/libnostr_core_x64.a -lcurses /usr/lib/x86_64-linux-gnu/libform.so -lsecp256k1 -lssl -lcrypto -lcurl -lz -lpthread -lm -ldl

View File

@@ -1,3 +1,5 @@
CMAKE_PROGRESS_1 = 1
CMAKE_PROGRESS_2 = 2
CMAKE_PROGRESS_3 = 3
CMAKE_PROGRESS_4 = 4

View File

@@ -34,6 +34,13 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o: \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdbool.h /usr/include/unctrl.h \
/usr/include/curses.h /usr/include/stdlib.h \
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h /usr/include/string.h \
/usr/include/x86_64-linux-gnu/sys/time.h \
/usr/include/x86_64-linux-gnu/bits/types/time_t.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \
/usr/include/x86_64-linux-gnu/sys/select.h \
/usr/include/x86_64-linux-gnu/bits/select.h \
/usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h \
/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/nostr_core/nostr_core.h \
/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/nostr_core/nostr_common.h \
@@ -44,7 +51,6 @@ CMakeFiles/nostr-id-tui.dir/src/main.c.o: \
/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/nostr_core/nip001.h \
/usr/include/time.h /usr/include/x86_64-linux-gnu/bits/time.h \
/usr/include/x86_64-linux-gnu/bits/types/clock_t.h \
/usr/include/x86_64-linux-gnu/bits/types/time_t.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \
/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/nostr_core/../cjson/cJSON.h \
/home/user/lt/n_os_tr/stack/nostr-id-tui/../../includes/nostr_core_lib/nostr_core/nip003.h \

View File

@@ -0,0 +1,29 @@
CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o: \
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/services_conf.c \
/usr/include/stdc-predef.h /usr/include/ctype.h /usr/include/features.h \
/usr/include/features-time64.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/bits/timesize.h \
/usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/long-double.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h \
/usr/include/x86_64-linux-gnu/bits/time64.h \
/usr/include/x86_64-linux-gnu/bits/endian.h \
/usr/include/x86_64-linux-gnu/bits/endianness.h /usr/include/stdio.h \
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/floatn.h \
/usr/include/x86_64-linux-gnu/bits/floatn-common.h /usr/include/stdlib.h \
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h /usr/include/string.h \
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h

View File

@@ -0,0 +1,36 @@
CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o: \
/home/user/lt/n_os_tr/stack/nostr-id-tui/src/systemd_client.c \
/usr/include/stdc-predef.h /usr/include/errno.h /usr/include/features.h \
/usr/include/features-time64.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/bits/timesize.h \
/usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/long-double.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/bits/errno.h /usr/include/linux/errno.h \
/usr/lib/linux/uapi/x86/asm/errno.h /usr/include/asm-generic/errno.h \
/usr/include/asm-generic/errno-base.h /usr/include/stdio.h \
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h \
/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h \
/usr/include/x86_64-linux-gnu/bits/time64.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/floatn.h \
/usr/include/x86_64-linux-gnu/bits/floatn-common.h /usr/include/stdlib.h \
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h /usr/include/string.h \
/usr/include/x86_64-linux-gnu/sys/wait.h \
/usr/include/x86_64-linux-gnu/bits/waitflags.h \
/usr/include/x86_64-linux-gnu/bits/waitstatus.h /usr/include/unistd.h \
/usr/include/x86_64-linux-gnu/bits/posix_opt.h \
/usr/include/x86_64-linux-gnu/bits/confname.h \
/usr/include/x86_64-linux-gnu/bits/unistd_ext.h \
/home/user/lt/n_os_tr/stack/nostr-id-tui/include/n_os_tr_tui.h

View File

@@ -153,6 +153,54 @@ src/main.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/src/main.c.s
.PHONY : src/main.c.s
src/services_conf.o: src/services_conf.c.o
.PHONY : src/services_conf.o
# target to build an object file
src/services_conf.c.o:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/src/services_conf.c.o
.PHONY : src/services_conf.c.o
src/services_conf.i: src/services_conf.c.i
.PHONY : src/services_conf.i
# target to preprocess a source file
src/services_conf.c.i:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/src/services_conf.c.i
.PHONY : src/services_conf.c.i
src/services_conf.s: src/services_conf.c.s
.PHONY : src/services_conf.s
# target to generate assembly for a file
src/services_conf.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/src/services_conf.c.s
.PHONY : src/services_conf.c.s
src/systemd_client.o: src/systemd_client.c.o
.PHONY : src/systemd_client.o
# target to build an object file
src/systemd_client.c.o:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.o
.PHONY : src/systemd_client.c.o
src/systemd_client.i: src/systemd_client.c.i
.PHONY : src/systemd_client.i
# target to preprocess a source file
src/systemd_client.c.i:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.i
.PHONY : src/systemd_client.c.i
src/systemd_client.s: src/systemd_client.c.s
.PHONY : src/systemd_client.s
# target to generate assembly for a file
src/systemd_client.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/nostr-id-tui.dir/build.make CMakeFiles/nostr-id-tui.dir/src/systemd_client.c.s
.PHONY : src/systemd_client.c.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@@ -165,6 +213,12 @@ help:
@echo "... src/main.o"
@echo "... src/main.i"
@echo "... src/main.s"
@echo "... src/services_conf.o"
@echo "... src/services_conf.i"
@echo "... src/services_conf.s"
@echo "... src/systemd_client.o"
@echo "... src/systemd_client.i"
@echo "... src/systemd_client.s"
.PHONY : help

View File

@@ -0,0 +1,2 @@
[?1049h(B[?7h[?1h=[?25l= n-OS-tr Identity ==TUI version: 0.2.0^[[4m1^[[0m) Enter existing mnemonic^[[4m2^[[0m) Generate new mnemonicE^[[4mx^[[0mitSelect option: > = n-OS-tr Identity ==TUI version: 0.2.0^[[4m1^[[0m) Enter existing mnemonic^[[4m2^[[0m) Generate new mnemonicE^[[4mx^[[0mitSelect option: >
Session terminated, killing shell... ...killed.

View File

@@ -1,9 +1,32 @@
#ifndef N_OS_TR_TUI_H
#define N_OS_TR_TUI_H
#define N_OS_TR_TUI_VERSION "0.1.0"
#include <stddef.h>
#define N_OS_TR_TUI_VERSION "0.2.0"
#define NOSTR_TUI_MAX_MNEMONIC 256
#define NOSTR_TUI_MAX_NPUB 128
#define N_OS_TR_TUI_MAX_SERVICES 16
#define N_OS_TR_TUI_OK 0
#define N_OS_TR_TUI_ERR 1
typedef struct {
char key;
char unit[64];
char label[64];
int default_on;
char deps[128];
int desired_on;
} service_entry_t;
int services_conf_load(const char* path, service_entry_t* out, size_t max, size_t* out_count);
int services_conf_load_fallback(service_entry_t* out, size_t max, size_t* out_count);
int systemd_is_simulation_mode(void);
int systemd_unit_is_active(const char* unit, int* is_active);
int systemd_unit_start(const char* unit, char* err_out, size_t err_out_len);
int systemd_unit_stop(const char* unit, char* err_out, size_t err_out_len);
#endif /* N_OS_TR_TUI_H */

View File

@@ -4,6 +4,8 @@
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "n_os_tr_tui.h"
#include "nostr_core/nostr_core.h"
@@ -11,29 +13,180 @@ typedef enum {
SCREEN_MENU = 0,
SCREEN_ENTER = 1,
SCREEN_GENERATE = 2,
SCREEN_EXIT = 3
SCREEN_SERVICES = 3,
SCREEN_EXIT = 4
} screen_t;
static void clear_and_title(const char* title) {
clear();
mvprintw(0, 2, "n-OS-tr nostr-id-tui v%s", N_OS_TR_TUI_VERSION);
mvprintw(1, 2, "%s", title);
mvhline(2, 1, ACS_HLINE, 76);
typedef struct {
int identity_loaded;
char main_npub[NOSTR_TUI_MAX_NPUB];
service_entry_t services[N_OS_TR_TUI_MAX_SERVICES];
size_t service_count;
} app_state_t;
#define BANNER_HEIGHT 9
#define BANNER_IN_MS 3000LL
#define BANNER_HOLD_MS 3000LL
#define BANNER_OUT_MS 3000LL
#define BANNER_PAUSE_MS 3000LL
#define BANNER_CYCLE_MS (BANNER_IN_MS + BANNER_HOLD_MS + BANNER_OUT_MS + BANNER_PAUSE_MS)
static const char* BANNER_ART[BANNER_HEIGHT] = {
"____________________________/\\\\\\\\\\__________/\\\\\\\\\\\\\\\\\\\\\\______________________________________________",
"___________________________/\\\\\\///\\\\\\______/\\\\\\/////////\\\\\\___________________________________________",
"__________________________/\\\\\\/__\\///\\\\\\___\\//\\\\\\______\\///_________________/\\\\\\______________________",
"_/\\\\/\\\\\\\\\\\\_______________/\\\\\\______\\//\\\\\\___\\////\\\\\\_____________________/\\\\\\\\\\\\\\\\\\\\\\__/\\\\/\\\\\\\\\\\\\\___",
"_\\/\\\\\\////\\\\\\_____________\\/\\\\\\_______\\/\\\\\\______\\////\\\\\\_________________\\////\\\\\\////__\\/\\\\\\/////\\\\\\_",
"__\\/\\\\\\__\\//\\\\\\____________\\//\\\\\\______/\\\\\\__________\\////\\\\\\_________________\\/\\\\\\______\\/\\\\\\___\\///_",
"___\\/\\\\\\___\\/\\\\\\_____________\\///\\\\\\__/\\\\\\_____/\\\\\\______\\//\\\\\\________________\\/\\\\\\_/\\\\__\\/\\\\\\_______",
"____\\/\\\\\\___\\/\\\\\\__/\\\\\\\\\\\\\\\\____\\///\\\\\\\\\\/_____\\///\\\\\\\\\\\\\\\\\\\\\\/____/\\\\\\\\\\\\\\\\____\\//\\\\\\\\\\___\\/\\\\\\______",
"_____\\///____\\///__\\////////_______\\/////_________\\///////////_____\\////////______\\/////____\\///______"
};
// Unescaped editable banner reference (do not compile directly):
//
// ____________________________/\\\\\__________/\\\\\\\\\\\______________________________________________
// ___________________________/\\\///\\\______/\\\/////////\\\___________________________________________
// __________________________/\\\/__\///\\\___\//\\\______\///_________________/\\\______________________
// _/\\/\\\\\\_______________/\\\______\//\\\___\////\\\_____________________/\\\\\\\\\\\__/\\/\\\\\\\___
// _\/\\\////\\\_____________\/\\\_______\/\\\______\////\\\_________________\////\\\////__\/\\\/////\\\_
//___\/\\\__\//\\\____________\//\\\______/\\\__________\////\\\_________________\/\\\______\/\\\___\///_
// ___\/\\\___\/\\\_____________\///\\\__/\\\_____/\\\______\//\\\________________\/\\\_/\\__\/\\\_______
// ____\/\\\___\/\\\__/\\\\\\\\____\///\\\\\/_____\///\\\\\\\\\\\/____/\\\\\\\\____\//\\\\\___\/\\\______
// _____\///____\///__\////////_______\/////_________\///////////_____\////////______\/////____\///______
static int ui_row(int y) {
return y + BANNER_HEIGHT + 1;
}
static long long monotonic_ms(void) {
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0) {
return 0;
}
return ((long long)tv.tv_sec * 1000LL) + ((long long)tv.tv_usec / 1000LL);
}
static int banner_art_width(void) {
int i;
int max_width = 0;
for (i = 0; i < BANNER_HEIGHT; ++i) {
int w = (int)strlen(BANNER_ART[i]);
if (w > max_width) {
max_width = w;
}
}
return max_width;
}
static void render_banner_frame(void) {
int row;
int col;
int screen_w = COLS;
int art_w = banner_art_width();
int center_x = (screen_w - art_w) / 2;
long long t = monotonic_ms() % BANNER_CYCLE_MS;
int show_art = 1;
int art_x = center_x;
for (row = 0; row < BANNER_HEIGHT; ++row) {
move(row, 0);
for (col = 0; col < screen_w; ++col) {
addch('_');
}
}
if (t < BANNER_IN_MS) {
int delta = center_x + art_w;
art_x = -art_w + (int)((delta * t) / BANNER_IN_MS);
} else if (t < (BANNER_IN_MS + BANNER_HOLD_MS)) {
art_x = center_x;
} else if (t < (BANNER_IN_MS + BANNER_HOLD_MS + BANNER_OUT_MS)) {
long long out_t = t - (BANNER_IN_MS + BANNER_HOLD_MS);
int delta = (screen_w + 1) - center_x;
art_x = center_x + (int)((delta * out_t) / BANNER_OUT_MS);
} else {
show_art = 0;
}
if (!show_art) {
return;
}
for (row = 0; row < BANNER_HEIGHT; ++row) {
const char* line = BANNER_ART[row];
int len = (int)strlen(line);
for (col = 0; col < len; ++col) {
int x = art_x + col;
char ch = line[col];
if (x < 0 || x >= screen_w) continue;
mvaddch(row, x, ch);
}
}
}
static void trim_line(char* s) {
size_t len;
if (!s) return;
len = strlen(s);
while (len > 0 && (s[len - 1] == '\n' || s[len - 1] == '\r' || isspace((unsigned char)s[len - 1]))) {
s[len - 1] = '\0';
len--;
}
while (*s && isspace((unsigned char)*s)) {
memmove(s, s + 1, strlen(s));
}
}
static void draw_centered_header(const char* title) {
int width = COLS;
int title_len = (int)strlen(title);
int min_required = title_len + 4;
int left_pad, right_pad, i;
int r;
/* Clear UI body when entering/redrawing a screen to avoid stale text
from previous screens (menu/generate/services overlays). */
for (r = ui_row(1); r < LINES; ++r) {
move(r, 0);
clrtoeol();
}
/* Header line stays independently drawn below the banner. */
move(ui_row(0), 0);
for (i = 0; i < COLS; ++i) addch(' ');
if (width < min_required) {
mvprintw(ui_row(0), 0, "=== %s ===", title);
return;
}
left_pad = (width - title_len - 2) / 2;
right_pad = width - title_len - 2 - left_pad;
move(ui_row(0), 0);
for (i = 0; i < left_pad; ++i) addch('=');
addch(' ');
addstr(title);
addch(' ');
for (i = 0; i < right_pad; ++i) addch('=');
}
static int read_line_at(int y, int x, char* out, size_t out_size) {
int rc;
if (!out || out_size < 2) return NOSTR_ERROR_INVALID_INPUT;
@@ -52,8 +205,15 @@ static int read_line_at(int y, int x, char* out, size_t out_size) {
}
static void wait_key(void) {
timeout(60);
mvprintw(LINES - 2, 2, "Press any key to continue...");
getch();
refresh();
while (getch() == ERR) {
render_banner_frame();
mvprintw(LINES - 2, 2, "Press any key to continue...");
refresh();
}
timeout(-1);
}
static int derive_from_mnemonic(const char* mnemonic, char* npub_out, size_t npub_size) {
@@ -73,9 +233,7 @@ static int derive_from_mnemonic(const char* mnemonic, char* npub_out, size_t npu
memset(priv, 0, sizeof(priv));
memset(pub, 0, sizeof(pub));
if (rc != NOSTR_SUCCESS) {
return rc;
}
if (rc != NOSTR_SUCCESS) return rc;
npub_out[npub_size - 1] = '\0';
return NOSTR_SUCCESS;
@@ -91,49 +249,193 @@ static void render_prefix_hint(const char* word_prefix) {
return;
}
mvprintw(11, 2, "Suggestions for '%s':", word_prefix);
mvprintw(ui_row(11), 2, "Suggestions for '%s':", word_prefix);
for (size_t i = 0; i < count && i < 6; ++i) {
mvprintw(12 + (int)i, 4, "- %s", matches[i]);
mvprintw(ui_row(12 + (int)i), 4, "- %s", matches[i]);
}
}
static void load_services_if_needed(app_state_t* app) {
const char* path;
if (!app || app->service_count > 0) return;
path = getenv("NOSTR_TUI_SERVICES_CONF");
if (!path || path[0] == '\0') {
path = "/etc/n-os-tr/services.conf";
}
if (services_conf_load(path, app->services, N_OS_TR_TUI_MAX_SERVICES, &app->service_count) != N_OS_TR_TUI_OK) {
(void)services_conf_load_fallback(app->services, N_OS_TR_TUI_MAX_SERVICES, &app->service_count);
}
}
static void refresh_service_states(app_state_t* app, int* active_states) {
size_t i;
int active = 0;
for (i = 0; i < app->service_count; ++i) {
if (systemd_unit_is_active(app->services[i].unit, &active) != N_OS_TR_TUI_OK) {
active = 0;
}
active_states[i] = active;
}
}
static void apply_dependency_rules(app_state_t* app, size_t idx) {
size_t i;
if (!app || idx >= app->service_count) return;
if (strcmp(app->services[idx].deps, "-") == 0 || app->services[idx].deps[0] == '\0') {
return;
}
if (!app->services[idx].desired_on) return;
for (i = 0; i < app->service_count; ++i) {
if (strcmp(app->services[i].unit, app->services[idx].deps) == 0) {
app->services[i].desired_on = 1;
}
}
}
static int apply_services(app_state_t* app) {
int y = ui_row(9);
size_t i;
int active_states[N_OS_TR_TUI_MAX_SERVICES];
char err[256] = {0};
int had_error = 0;
refresh_service_states(app, active_states);
mvprintw(ui_row(7), 2, "Applying selection...");
for (i = 0; i < app->service_count; ++i) {
int rc;
const char* action = NULL;
if (app->services[i].desired_on && !active_states[i]) {
action = "START";
rc = systemd_unit_start(app->services[i].unit, err, sizeof(err));
} else if (!app->services[i].desired_on && active_states[i]) {
action = "STOP ";
rc = systemd_unit_stop(app->services[i].unit, err, sizeof(err));
} else {
action = "SKIP ";
rc = N_OS_TR_TUI_OK;
}
if (rc == N_OS_TR_TUI_OK) {
mvprintw(y++, 4, "[ OK ] %s %s", action, app->services[i].unit);
} else {
mvprintw(y++, 4, "[FAIL] %s %s", action, app->services[i].unit);
mvprintw(y++, 6, "%s", err[0] ? err : "systemctl error");
had_error = 1;
}
if (y >= LINES - 4) break;
}
if (had_error) {
mvprintw(LINES - 4, 2, "One or more unit operations failed.");
return N_OS_TR_TUI_ERR;
}
mvprintw(LINES - 4, 2, "Service selection applied.");
return N_OS_TR_TUI_OK;
}
static void render_menu_static(void) {
draw_centered_header("n_OS_tr");
move(ui_row(4), 2);
attron(A_UNDERLINE);
addch('E');
attroff(A_UNDERLINE);
addstr("nter existing mnemonic");
move(ui_row(5), 2);
attron(A_UNDERLINE);
addch('G');
attroff(A_UNDERLINE);
addstr("enerate new mnemonic");
move(ui_row(6), 2);
addch('E');
attron(A_UNDERLINE);
addch('x');
attroff(A_UNDERLINE);
addstr("it");
mvprintw(ui_row(8), 2, "Select action: > ");
}
static screen_t screen_menu(void) {
int ch;
clear_and_title("Main Menu");
mvprintw(4, 2, "1) Enter existing mnemonic");
mvprintw(5, 2, "2) Generate new mnemonic");
mvprintw(6, 2, "q) Quit");
mvprintw(8, 2, "Select option: ");
int last_cols = COLS;
int last_lines = LINES;
render_menu_static();
render_banner_frame();
refresh();
timeout(60);
while (1) {
ch = getch();
if (ch == '1') return SCREEN_ENTER;
if (ch == '2') return SCREEN_GENERATE;
if (ch == 'q' || ch == 'Q') return SCREEN_EXIT;
return SCREEN_MENU;
if (ch == ERR) {
if (COLS != last_cols || LINES != last_lines) {
last_cols = COLS;
last_lines = LINES;
render_menu_static();
}
render_banner_frame();
refresh();
continue;
}
static screen_t screen_enter(void) {
if (ch == KEY_RESIZE) {
last_cols = COLS;
last_lines = LINES;
render_menu_static();
render_banner_frame();
refresh();
continue;
}
ch = tolower(ch);
if (ch == 'e') {
timeout(-1);
return SCREEN_ENTER;
}
if (ch == 'g') {
timeout(-1);
return SCREEN_GENERATE;
}
if (ch == 'x' || ch == 'q') {
timeout(-1);
return SCREEN_EXIT;
}
}
}
static screen_t screen_enter(app_state_t* app) {
char line[NOSTR_TUI_MAX_MNEMONIC] = {0};
char npub[NOSTR_TUI_MAX_NPUB] = {0};
nostr_secure_buf_t secure = {0};
int rc;
clear_and_title("Enter mnemonic");
mvprintw(4, 2, "Paste your 12/15/18/21/24-word mnemonic and press Enter.");
mvprintw(5, 2, "Mnemonic: ");
draw_centered_header("Enter Mnemonic");
mvprintw(ui_row(3), 2, "Paste your 12/15/18/21/24-word mnemonic and press Enter.");
mvprintw(ui_row(5), 2, "Mnemonic: ");
refresh();
rc = nostr_secure_buf_init(&secure, NOSTR_TUI_MAX_MNEMONIC);
if (rc != NOSTR_SUCCESS) {
mvprintw(7, 2, "secure memory init failed: %d", rc);
mvprintw(ui_row(7), 2, "secure memory init failed: %d", rc);
wait_key();
return SCREEN_MENU;
}
rc = read_line_at(5, 12, line, sizeof(line));
rc = read_line_at(ui_row(5), 12, line, sizeof(line));
if (rc != NOSTR_SUCCESS) {
mvprintw(7, 2, "input error");
mvprintw(ui_row(7), 2, "input error");
nostr_secure_buf_destroy(&secure);
wait_key();
return SCREEN_MENU;
@@ -141,98 +443,261 @@ static screen_t screen_enter(void) {
rc = nostr_secure_buf_append(&secure, line, strlen(line));
if (rc != NOSTR_SUCCESS) {
mvprintw(7, 2, "secure buffer append failed: %d", rc);
mvprintw(ui_row(7), 2, "secure buffer append failed: %d", rc);
nostr_secure_buf_destroy(&secure);
wait_key();
return SCREEN_MENU;
}
if (nostr_bip39_mnemonic_validate((const char*)secure.bytes) != NOSTR_SUCCESS) {
mvprintw(7, 2, "Invalid mnemonic.");
char first_word[32] = {0};
size_t i = 0;
mvprintw(ui_row(7), 2, "Invalid mnemonic.");
while (i < sizeof(first_word) - 1 && line[i] && !isspace((unsigned char)line[i])) {
first_word[i] = line[i];
i++;
}
first_word[i] = '\0';
render_prefix_hint(first_word);
nostr_secure_buf_destroy(&secure);
wait_key();
return SCREEN_MENU;
}
rc = derive_from_mnemonic((const char*)secure.bytes, npub, sizeof(npub));
if (rc != NOSTR_SUCCESS) {
mvprintw(7, 2, "Key derivation failed: %d", rc);
nostr_secure_buf_destroy(&secure);
wait_key();
return SCREEN_MENU;
}
mvprintw(7, 2, "Mnemonic accepted.");
mvprintw(8, 2, "Derived main npub:");
mvprintw(9, 2, "%s", npub);
rc = derive_from_mnemonic((const char*)secure.bytes, app->main_npub, sizeof(app->main_npub));
nostr_secure_buf_destroy(&secure);
memset(line, 0, sizeof(line));
if (rc != NOSTR_SUCCESS) {
mvprintw(ui_row(7), 2, "Key derivation failed: %d", rc);
wait_key();
return SCREEN_MENU;
}
static screen_t screen_generate(void) {
app->identity_loaded = 1;
return SCREEN_SERVICES;
}
static screen_t screen_generate(app_state_t* app) {
char mnemonic[NOSTR_TUI_MAX_MNEMONIC] = {0};
char npub[NOSTR_TUI_MAX_NPUB] = {0};
char main_nsec[NOSTR_TUI_MAX_NPUB] = {0};
unsigned char priv[32] = {0};
unsigned char pub[32] = {0};
uint8_t picks[NOSTR_BIP39_CONFIRM_PICK_COUNT] = {0};
int rc;
clear_and_title("Generate mnemonic");
draw_centered_header("Generate Mnemonic");
rc = nostr_generate_mnemonic_and_keys(mnemonic, sizeof(mnemonic), 0, priv, pub);
if (rc != NOSTR_SUCCESS) {
mvprintw(4, 2, "Generation failed: %d", rc);
mvprintw(ui_row(4), 2, "Generation failed: %d", rc);
wait_key();
return SCREEN_MENU;
}
rc = nostr_key_to_bech32(pub, "npub", npub);
rc = nostr_key_to_bech32(pub, "npub", app->main_npub);
if (rc != NOSTR_SUCCESS) {
mvprintw(4, 2, "npub conversion failed: %d", rc);
mvprintw(ui_row(4), 2, "npub conversion failed: %d", rc);
memset(priv, 0, sizeof(priv));
memset(pub, 0, sizeof(pub));
wait_key();
return SCREEN_MENU;
}
rc = nostr_bip39_ui_random_confirm_positions(12, picks);
rc = nostr_key_to_bech32(priv, "nsec", main_nsec);
if (rc != NOSTR_SUCCESS) {
mvprintw(4, 2, "confirm-position generation failed: %d", rc);
mvprintw(ui_row(4), 2, "nsec conversion failed: %d", rc);
memset(priv, 0, sizeof(priv));
memset(pub, 0, sizeof(pub));
wait_key();
return SCREEN_MENU;
}
mvprintw(4, 2, "Write this mnemonic down now:");
mvprintw(6, 2, "%s", mnemonic);
mvprintw(8, 2, "Derived main npub:");
mvprintw(9, 2, "%s", npub);
mvprintw(11, 2, "Confirmation check words: #%u, #%u, #%u",
(unsigned)picks[0], (unsigned)picks[1], (unsigned)picks[2]);
mvprintw(ui_row(4), 2, "Write this mnemonic down now:");
mvprintw(ui_row(5), 2, "Mnemonic (index 0):");
mvprintw(ui_row(6), 2, "%s", mnemonic);
mvprintw(ui_row(8), 2, "Derived keys (mnemonic index 0):");
mvprintw(ui_row(9), 2, "npub: %s", app->main_npub);
mvprintw(ui_row(10), 2, "nsec: %s", main_nsec);
memset(priv, 0, sizeof(priv));
memset(pub, 0, sizeof(pub));
memset(mnemonic, 0, sizeof(mnemonic));
app->identity_loaded = 1;
wait_key();
return SCREEN_SERVICES;
}
static void render_underlined_hotkey_service_row(int y,
const service_entry_t* svc,
int active_state) {
int x = 3;
const char* label;
const char* key_pos;
size_t prefix_len;
if (!svc) return;
label = svc->label;
key_pos = strchr(label, (int)tolower((unsigned char)svc->key));
if (!key_pos) {
key_pos = strchr(label, (int)toupper((unsigned char)svc->key));
}
if (!key_pos) {
mvprintw(y, x, "%-16s [ %s ] [ %s ] %s",
label,
svc->desired_on ? "on " : "off",
active_state ? "on " : "off",
svc->unit);
return;
}
prefix_len = (size_t)(key_pos - label);
move(y, x);
addnstr(label, (int)prefix_len);
x += (int)prefix_len;
move(y, x);
attron(A_UNDERLINE);
addch(*key_pos);
attroff(A_UNDERLINE);
x += 1;
move(y, x);
addstr(key_pos + 1);
mvprintw(y, 22, "[ %s ] [ %s ] %s",
svc->desired_on ? "on " : "off",
active_state ? "on " : "off",
svc->unit);
}
static void render_services_table(app_state_t* app, const int* active_states) {
size_t i;
int y = ui_row(6);
mvprintw(ui_row(4), 2, "Service Desired Active Unit");
mvprintw(ui_row(5), 2, "----------------- ------- ------ ------------------------------");
for (i = 0; i < app->service_count && y < LINES - 8; ++i) {
render_underlined_hotkey_service_row(y++, &app->services[i], active_states[i]);
}
}
static screen_t screen_services(app_state_t* app) {
int ch;
int active_states[N_OS_TR_TUI_MAX_SERVICES] = {0};
size_t i;
int needs_redraw = 1;
int last_cols = COLS;
int last_lines = LINES;
if (!app || !app->identity_loaded) {
return SCREEN_MENU;
}
load_services_if_needed(app);
for (i = 0; i < app->service_count; ++i) {
app->services[i].desired_on = app->services[i].default_on;
}
timeout(60);
while (1) {
if (needs_redraw) {
draw_centered_header("Services");
refresh_service_states(app, active_states);
mvprintw(ui_row(2), 2, "Identity: %.22s...", app->main_npub[0] ? app->main_npub : "(unknown)");
mvprintw(ui_row(3), 2, "Simulation mode: %s", systemd_is_simulation_mode() ? "ON" : "OFF");
render_services_table(app, active_states);
mvprintw(LINES - 6, 2, "Actions:");
mvprintw(LINES - 5, 2, " R/G/F/N/L/W/S/T toggle | A all on | Z all off");
mvprintw(LINES - 4, 2, " C commit/apply | X skip (leave unchanged) | Q quit");
mvprintw(LINES - 2, 2, "Select action: > ");
render_banner_frame();
refresh();
needs_redraw = 0;
}
ch = getch();
if (ch == ERR) {
if (COLS != last_cols || LINES != last_lines) {
last_cols = COLS;
last_lines = LINES;
needs_redraw = 1;
}
render_banner_frame();
refresh();
continue;
}
if (ch == KEY_RESIZE) {
last_cols = COLS;
last_lines = LINES;
needs_redraw = 1;
continue;
}
ch = tolower(ch);
if (ch == 'q') {
timeout(-1);
return SCREEN_EXIT;
}
if (ch == 'x') {
timeout(-1);
return SCREEN_EXIT;
}
if (ch == 'a') {
for (i = 0; i < app->service_count; ++i) app->services[i].desired_on = 1;
needs_redraw = 1;
continue;
}
if (ch == 'z') {
for (i = 0; i < app->service_count; ++i) app->services[i].desired_on = 0;
needs_redraw = 1;
continue;
}
if (ch == 'c') {
draw_centered_header("Applying Services");
render_banner_frame();
mvprintw(ui_row(2), 2, "Committing selected service states...");
if (apply_services(app) != N_OS_TR_TUI_OK) {
wait_key();
needs_redraw = 1;
continue;
}
wait_key();
timeout(-1);
return SCREEN_EXIT;
}
for (i = 0; i < app->service_count; ++i) {
if (ch == tolower((unsigned char)app->services[i].key)) {
app->services[i].desired_on = !app->services[i].desired_on;
apply_dependency_rules(app, i);
needs_redraw = 1;
break;
}
}
}
}
int main(void) {
screen_t screen = SCREEN_MENU;
app_state_t app;
memset(&app, 0, sizeof(app));
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "nostr_init failed\n");
@@ -254,10 +719,13 @@ int main(void) {
screen = screen_menu();
break;
case SCREEN_ENTER:
screen = screen_enter();
screen = screen_enter(&app);
break;
case SCREEN_GENERATE:
screen = screen_generate();
screen = screen_generate(&app);
break;
case SCREEN_SERVICES:
screen = screen_services(&app);
break;
case SCREEN_EXIT:
default:

View File

@@ -0,0 +1,131 @@
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "n_os_tr_tui.h"
static void copy_str(char* dst, size_t dst_len, const char* src) {
if (!dst || dst_len == 0) return;
if (!src) {
dst[0] = '\0';
return;
}
snprintf(dst, dst_len, "%s", src);
}
static void trim(char* s) {
size_t len;
if (!s) return;
while (*s && isspace((unsigned char)*s)) {
memmove(s, s + 1, strlen(s));
}
len = strlen(s);
while (len > 0 && isspace((unsigned char)s[len - 1])) {
s[len - 1] = '\0';
len--;
}
}
static int parse_default(const char* v) {
if (!v) return 0;
if (strcmp(v, "on") == 0 || strcmp(v, "1") == 0 || strcmp(v, "true") == 0) return 1;
return 0;
}
static void set_entry(service_entry_t* e,
char key,
const char* unit,
const char* label,
int default_on,
const char* deps) {
if (!e) return;
memset(e, 0, sizeof(*e));
e->key = key;
copy_str(e->unit, sizeof(e->unit), unit);
copy_str(e->label, sizeof(e->label), label);
e->default_on = default_on;
copy_str(e->deps, sizeof(e->deps), deps ? deps : "-");
e->desired_on = default_on;
}
int services_conf_load_fallback(service_entry_t* out, size_t max, size_t* out_count) {
size_t i = 0;
if (!out || !out_count || max < 8) return N_OS_TR_TUI_ERR;
set_entry(&out[i++], 'r', "c-relay.service", "c-relay", 0, "nginx.service");
set_entry(&out[i++], 'g', "ginxsom.service", "ginxsom", 0, "nginx.service");
set_entry(&out[i++], 'f', "fips.service", "fips", 0, "-");
set_entry(&out[i++], 'n', "nginx.service", "nginx", 0, "-");
set_entry(&out[i++], 'l', "nostr-config-loader.service", "config-loader", 0, "-");
set_entry(&out[i++], 'w', "nostr-config-writer.service", "config-writer", 0, "nostr-config-loader.service");
set_entry(&out[i++], 's', "ssh.service", "ssh", 0, "-");
set_entry(&out[i++], 't', "tor.service", "tor", 0, "-");
*out_count = i;
return N_OS_TR_TUI_OK;
}
int services_conf_load(const char* path, service_entry_t* out, size_t max, size_t* out_count) {
FILE* fp;
char line[512];
size_t n = 0;
if (!path || !out || !out_count || max == 0) return N_OS_TR_TUI_ERR;
fp = fopen(path, "r");
if (!fp) return N_OS_TR_TUI_ERR;
while (fgets(line, sizeof(line), fp) != NULL) {
char* tok;
char local[512];
char* key_s;
char* unit_s;
char* label_s;
char* def_s;
char* deps_s;
char key;
copy_str(local, sizeof(local), line);
trim(local);
if (local[0] == '\0' || local[0] == '#') continue;
tok = strtok(local, "\t ");
if (!tok) continue;
key_s = tok;
tok = strtok(NULL, "\t ");
if (!tok) continue;
unit_s = tok;
tok = strtok(NULL, "\t ");
if (!tok) continue;
label_s = tok;
tok = strtok(NULL, "\t ");
if (!tok) continue;
def_s = tok;
tok = strtok(NULL, "\t ");
deps_s = tok ? tok : "-";
key = (char)tolower((unsigned char)key_s[0]);
if (!isprint((unsigned char)key)) continue;
if (n >= max) {
fclose(fp);
return N_OS_TR_TUI_ERR;
}
set_entry(&out[n], key, unit_s, label_s, parse_default(def_s), deps_s);
n++;
}
fclose(fp);
if (n == 0) return N_OS_TR_TUI_ERR;
*out_count = n;
return N_OS_TR_TUI_OK;
}

View File

@@ -0,0 +1,100 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "n_os_tr_tui.h"
static int run_systemctl(char* const argv[]) {
pid_t pid;
int status = 0;
if (systemd_is_simulation_mode()) {
return 0;
}
if (access("/bin/systemctl", X_OK) != 0) {
return -1;
}
pid = fork();
if (pid < 0) {
return -1;
}
if (pid == 0) {
execv("/bin/systemctl", argv);
_exit(127);
}
if (waitpid(pid, &status, 0) < 0) {
return -1;
}
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
return -1;
}
int systemd_is_simulation_mode(void) {
const char* v = getenv("NOSTR_TUI_SIMULATE_SYSTEMCTL");
return (v && strcmp(v, "1") == 0) ? 1 : 0;
}
int systemd_unit_is_active(const char* unit, int* is_active) {
char* const argv[] = {
"/bin/systemctl", "is-active", "--quiet", (char*)unit, NULL
};
int rc;
if (!unit || !is_active) return N_OS_TR_TUI_ERR;
if (systemd_is_simulation_mode()) {
*is_active = 0;
return N_OS_TR_TUI_OK;
}
rc = run_systemctl(argv);
if (rc < 0) return N_OS_TR_TUI_ERR;
*is_active = (rc == 0) ? 1 : 0;
return N_OS_TR_TUI_OK;
}
int systemd_unit_start(const char* unit, char* err_out, size_t err_out_len) {
char* const argv[] = {
"/bin/systemctl", "start", (char*)unit, NULL
};
int rc;
if (!unit) return N_OS_TR_TUI_ERR;
rc = run_systemctl(argv);
if (rc == 0) return N_OS_TR_TUI_OK;
if (err_out && err_out_len > 0) {
snprintf(err_out, err_out_len, "systemctl start failed for %s (rc=%d, errno=%d)", unit, rc, errno);
}
return N_OS_TR_TUI_ERR;
}
int systemd_unit_stop(const char* unit, char* err_out, size_t err_out_len) {
char* const argv[] = {
"/bin/systemctl", "stop", (char*)unit, NULL
};
int rc;
if (!unit) return N_OS_TR_TUI_ERR;
rc = run_systemctl(argv);
if (rc == 0) return N_OS_TR_TUI_OK;
if (err_out && err_out_len > 0) {
snprintf(err_out, err_out_len, "systemctl stop failed for %s (rc=%d, errno=%d)", unit, rc, errno);
}
return N_OS_TR_TUI_ERR;
}

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
BIN="$ROOT_DIR/stack/nostr-id-tui/build-host/nostr-id-tui"
SERVICES_CONF="$ROOT_DIR/iso/config/includes.chroot/etc/n-os-tr/services.conf"
if [[ ! -x "$BIN" ]]; then
echo "error: missing binary $BIN"
echo "build first: cmake -S stack/nostr-id-tui -B stack/nostr-id-tui/build-host && cmake --build stack/nostr-id-tui/build-host -j"
exit 1
fi
if [[ ! -f "$SERVICES_CONF" ]]; then
echo "error: missing services config $SERVICES_CONF"
exit 1
fi
OUT="$ROOT_DIR/stack/nostr-id-tui/build-host/test-services-sim.out"
# Simulated run (no real systemctl operations). We just verify the TUI launches and renders.
timeout 2s env TERM=xterm-256color \
NOSTR_TUI_SIMULATE_SYSTEMCTL=1 \
NOSTR_TUI_SERVICES_CONF="$SERVICES_CONF" \
script -q -c "$BIN" /dev/null > "$OUT" 2>&1 || true
if grep -q "n-OS-tr Identity" "$OUT"; then
echo "ok: TUI launched and rendered header"
else
echo "error: expected TUI header not found in output"
echo "see $OUT"
exit 1
fi
echo "ok: simulated services smoke test passed"