vipirc 0.3.1 - build and installation

REQUIREMENTS

The target is Unix-like systems, including Linux in the ChromeOS Linux
environment, OpenBSD, FreeBSD, and other BSDs with the required interfaces.

    C11 compiler and standard C library
    POSIX.1 interfaces, including poll, sockets, fork, and descriptor I/O
    Descriptor-relative file operations and no-follow opens (openat,
        fstatat, mkdirat, O_NOFOLLOW), plus a monotonic clock
    GNU/BSD getopt_long option parsing (provided by common Linux/BSD libc)
    Wide-character curses headers and library (normally ncursesw)
    OpenSSL-compatible TLS development headers and libraries
    A configured certificate authority trust store for public TLS servers
    A make implementation and a POSIX shell

The installed client has no Python dependency. Python 3 is needed for the
integration tests. Groff or mandoc is useful for viewing the manual from
the source tree. The normal build does not require either documentation
formatter.

Multiple IRC sessions and DCC SEND use the same C/POSIX runtime and add no
libraries or language runtimes. DCC uses ordinary TCP sockets and file
descriptors; it does not need an external transfer program.

The implementation uses OpenSSL 1.1.1-or-newer-compatible APIs, including
certificate-hostname verification and a minimum TLS protocol setting.
LibreSSL can be used when its headers and libraries supply those APIs.
Using a supported TLS library from your operating system is recommended.

BUILD

From the extracted source directory:

    ./configure
    make

The executable is ./vipirc. Regenerate the Makefile with ./configure on each
target; a Makefile configured elsewhere can contain host-specific include
paths and library names. The configure script checks the compiler and
required libraries. It does not download software or inspect another IRC
client's source. Compiler and link options can be supplied through the
usual build variables. For example, a system with optional libraries under
/usr/local may need:

    CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib ./configure
    make

If library autodetection needs an explicit choice:

    CURSES_LIBS=-lncursesw TLS_LIBS='-lssl -lcrypto' ./configure
    make

Use the development package that supplies each header/library pair, not
only a runtime library package. On a Debian-family Linux installation,
these typically include build-essential, libncurses-dev, and libssl-dev;
ca-certificates supplies the standard public certificate trust material.
ChromeOS uses its Linux development environment for this build. The client
is a Unix executable, not an Android APK or browser extension.

One instance supports up to eight IRC server sessions in one terminal.
Use the Servers toolbar control or /servers to add, open, connect, disconnect,
or close a session. Each keeps separate connection/authentication settings,
channels, queries, drafts, filters, and history. A graphical desktop is not
required. Separate processes are still possible when explicitly wanted;
they are no longer required to connect to several servers.

BSD base systems often provide some of the compiler, curses, and TLS
components. Check the detected headers and link results before installing
additional packages. Avoid mixing headers from one TLS implementation
with another implementation's libraries.

Native portability must be validated on the target OS. A successful Linux
build does not prove that a particular BSD version has been tested. Linux is
the tested release platform; native OpenBSD and FreeBSD validation remains
to be done. Compiling or smoke-testing a curses fallback branch on Linux
does not count as a native BSD test. See the release validation report for
actual platforms, commands, and toolchain versions.

Resolver children must be able to close all inherited descriptors before
DNS lookup. The implementation uses Linux close_range or BSD closefrom,
with a fallback through a complete /proc/self/fd or /dev/fd listing and a
one-second deadline per directory scan.
If none is available, connection setup reports an isolation error instead
of doing lookup while retaining other sessions' sockets or transfer files.
The configured build does not by itself prove this runtime facility works
on an untested target.

RUN WITHOUT INSTALLING

    ./vipirc -h
    ./vipirc -v
    ./vipirc --manual
    ./vipirc --test-mode
    ./vipirc --server irc.ircnow.org --channel '#ircnow' --nick tutankh-aten

-h/--help and -v/--version print and exit immediately. -V is retained as a
version alias. --manual and --check-config also work without a terminal.

--test-mode runs the interface with bounded in-process IRC simulation.
It requires an interactive terminal but no socket, DNS, credentials, or
running IRC server. It uses the normal configuration and supplies test
channels when the channel list is empty. Missing nick/server values do
not prompt. Dismiss the welcome with an ordinary printable key, Enter, Tab,
or a navigation key. Terminal signal characters keep their meaning: Ctrl-C
exits with status 130 at the welcome, and Ctrl-Z suspends and resumes the
same screen after fg. Custom terminal signal settings are respected.

The interface starts in Navigation mode with the original keymap. Tab
focuses Input and returns to Navigation while preserving the draft.
Main-screen Escape opens Settings, preserving the draft and cancelling selection.
On the main toolbar, all four arrows move the highlight and Enter activates
the selected Servers, Channels, Queries, Mentions, Users, or Actions control.
The ? key in Navigation also opens Actions. Tab stays inside secondary
screens; Escape returns to the chat screen, where Tab can focus Input.
Page Up/Page Down and Home/End navigate history. The optional Vim-style
navigation
preset is selected with /keymap vim or keymap=vim in [interface]; original
is the default, and both presets start in Navigation. Input editing is
the same in both. Tab switches focus in both; the Vim preset also accepts i
to enter Input. The header shows the active mode and keymap.
Navigation focus uses reverse video by default. Actions or the config
focus_style=reverse|brackets setting can change its appearance.
Settings stages edits. Apply previews live preferences in memory; Save
writes the selected server's configuration. Connection edits require saving
and a new launch; they do not retarget an existing session or its reconnect.
The New server form instead adds an independent unsaved session. Save is
disabled in test mode.

Servers, Channels, and Queries show unread and mention counts. Server/MOTD
remains channel 0 inside every session. Disconnect retains that server's
views and drafts. Close is available only for secondary server sessions and
asks before discarding their state, cancelling their transfers, and removing
any saved startup entry. The physical profile file is kept. /quit or Actions
> Quit ends the application and all sessions. Ordinary Ctrl-D is ignored at
the welcome, startup prompts, and application screens, even in an empty field;
custom terminal signal mappings still take precedence.

Test an existing configuration interactively:

    ./vipirc --test-mode --config /absolute/config.ini

Each configured server is a label in this mode. /connect only starts its
simulator, /disconnect stops it, and --no-connect leaves every session
stopped at startup. Otherwise usable saved profiles start their simulators
regardless of saved autoconnect. Newly added sessions remain offline too.
/save is disabled, and closing a saved secondary does not write its catalog.
DCC has a sample offer for inspecting controls, but send/accept fail before
file or network access. Edit the config and restart to try saved changes.
No configuration is created automatically. The simulator cannot validate
real TLS certificates, account credentials, or network connectivity.
Run --check-config separately for syntax/value validation. Combining
--test-mode with --check-config or --init-config exits with usage status 2.

A UTF-8 locale is needed for predictable non-ASCII display widths. ASCII
text already uses one byte per character within UTF-8. When using SSH,
inspect the host running vipirc:

    locale
    locale charmap
    locale -a

The first two commands show the session's locale settings and character
encoding; the third lists installed locales. Use an installed UTF-8 locale
whose effective LC_CTYPE matches the terminal's encoding. Locale names
vary between Unix-like systems; a name available locally may be absent on
an SSH host. Check each host separately.
TERM describes terminal capabilities, not encoding, and should still match
the terminal emulator. The client does not detect terminal encoding or
transcode legacy encodings.

The active LC_CTYPE is respected. A UTF-8 locale enables Unicode editing.
A non-UTF-8 or invalid locale selects explicit ASCII terminal mode, with a
welcome note and persistent mode marker. ASCII input works; non-ASCII input
is refused with a status message. Incoming non-ASCII code points display
as '?' without changing the stored network text. For Unicode use, select
an installed UTF-8 locale and restart the client. The client does not change
the terminal's encoding or force a different locale.

INSTALL FOR THE CURRENT ACCOUNT

    make install PREFIX="$HOME/.local"

Ensure $HOME/.local/bin is in PATH. The manual page is installed below
$HOME/.local/share/man; your man implementation may need that directory
in its manual search path. PREFIX defaults are given by the Makefile.

INSTALL UNDER A SYSTEM PREFIX

    make install PREFIX=/usr/local

Writing a system prefix requires permission to that destination. Use your
operating system's usual administrative installation method when needed.
The program itself must run as an ordinary user; do not install it setuid
or setgid, and do not run it as root for ordinary IRC use.

PACKAGE STAGING

    make install PREFIX=/usr/local DESTDIR=/absolute/staging/directory

DESTDIR adds a packaging root. PREFIX remains the eventual installation
prefix. Keep both settings consistent when packaging or uninstalling.

CONFIGURATION

    vipirc --init-config --server irc.ircnow.org --channel '#ircnow'
    vipirc --check-config

The configuration defaults to $HOME/.config/vipirc/config.ini, or to
$XDG_CONFIG_HOME/vipirc/config.ini when that absolute environment value is
set. An explicit --config selects another file. --init-config refuses to
overwrite an existing file. See examples/config.ini and the full manual.

The selected primary path P also owns P.servers, a small catalog of up to
seven additional profile IDs, and P.d/ID, each profile's configuration file.
For the default path these are config.ini.servers and config.ini.d/ID beside
config.ini. A missing catalog means no additional saved servers. The catalog
contains IDs rather than arbitrary paths. --check-config checks the primary,
catalog, and all referenced profiles without connecting or reading secrets.

Servers > New offers Add (without connecting) or Connect; neither saves the
new session. Use /save or Settings Save on that session to make it discoverable
at the next launch. Each profile keeps its own auto-connect choice;
--no-connect suppresses all startup connections. Settings connection changes
are saved for the next launch, while live interface/filter changes can be
applied immediately. Failed profile loads are shown without preventing other
valid sessions from being used.

Saving a secondary writes its private profile and then updates the catalog
under a private nonblocking advisory lock. Individual files are replaced
atomically; a failed catalog update can leave an unlisted profile file.
Closing a saved secondary removes its catalog reference but does not delete
that file. A catalog-write failure keeps the session open for correction or
retry. Disconnect does not change saved entries. Existing primary configs
remain valid without a catalog, and newly added sessions inherit no server
password, SASL identity, or channel list from another server.

Earlier builds under the working name irccmd used a directory named irccmd
in the same configuration base. vipirc does not automatically read or move
that file. To reuse it, select its existing absolute path with --config,
or copy it into the new vipirc directory with the permissions described below.

The final directory for generated configuration must be owned by the
current user with mode 0700. The generated file has mode 0600. File paths
must not use symbolic links or '..' components. A separate secret file
normally has mode 0600 or 0400 and contains only the password.

DIRECT FILE TRANSFERS

Open File transfers through Actions, /dcc, or /transfers. One bounded list
covers every server session: eight pending incoming offers, four active
transfers including listeners/connect attempts, and twelve retained records.
The supported protocol is active IPv4 DCC SEND, with a known size from zero
through 4294967295 bytes. CHAT, resume, passive/reverse SEND, IPv6 endpoints,
and larger or unknown-size files are not supported.

An incoming offer does not open a file or connect. Select Accept to review
the peer IP, port, byte count, directory, and filename, then deliberately
choose Accept in the form. Reject opens nothing. The review needs at least
12 rows by 20 columns. Received files are new regular files with mode 0600;
existing files are never overwritten. Symlinks and '..' path components are
refused. The receive directory must be owned by the current user with no
group/other permissions. Only explicit Accept creates missing directory
components, with mode 0700. Files are never launched or executed automatically.

The proposed download directory is $XDG_DATA_HOME/vipirc/downloads when that
environment value is absolute, otherwise $HOME/.local/share/vipirc/downloads.
It is only a proposed path until acceptance. You can choose a different
private directory and safe filename. Cancellation/failure removes a partial
file only if it still matches the inode created by that transfer.

Sending requires a registered IRC session, recipient nickname, explicitly
selected regular source file, advertised IPv4 address reachable by the peer,
and listen port. Port 0 chooses an available local port. The listener uses
the running host's IPv4 interfaces; there is no public-address discovery,
UPnP, automatic NAT forwarding, or firewall configuration. A forwarded port
must match the advertised port. The source is not copied or locked against
later modification; select a stable file.

DCC transfers are unencrypted and do not authenticate the direct peer as
the IRC nickname, even when IRC uses TLS. They reveal a network address to
the peer. On SSH, paths, HOME/XDG defaults, listeners, and peer connections
belong to the host running vipirc, not the terminal's local computer. To
send a local computer's file from a remote session, first arrange for that
file to exist on the remote host through your usual file-transfer method.

Transfers progress while chat remains usable. Offers expire after five
minutes; endpoints fail after two minutes without progress. Cancel removes
an unsent IRC advertisement, but a frame already in flight cannot be recalled.
Disconnecting IRC leaves accepted direct transfers running. Closing their
originating secondary server or quitting cancels them. See SECURITY.md for
the file-handling, endpoint, and partial-file cleanup boundaries.

TESTS

    make check-unit

This runs protocol, editor, configuration/catalog, App, client, and DCC engine
checks. The DCC cases use local loopback sockets and temporary files, so this
target needs loopback permission even though its other model tests do not.

    make check-offline

This runs command-line and real-terminal-PTY checks without a network
socket, including the built-in offline mode. It requires Python 3 for the
test harness; the installed --test-mode does not require Python.
Server-list and file-transfer preview cases are included, with real transfer
send/accept disabled before file or socket I/O.

    make check

The suite runs the unit and offline checks plus local fake-server and file
transfer integration. These broader tests use loopback and temporary data.
They do not join a public IRC channel or send messages to other users.
Server tests cover routing, unread/mentions, drafts, and catalog persistence;
transfer tests cover explicit consent, binary/empty files, fragmented ACKs,
cancellation, and responsiveness of multiple IRC sessions during transfers.

For focused server/transfer terminal tests after building:

    python3 tests/test_servers.py --offline-only
    python3 tests/test_transfers.py --offline-only
    python3 tests/test_servers.py --live-only
    python3 tests/test_transfers.py --live-only

The live-only cases use local fixtures, not public servers. Omitting a mode
flag runs both parts of that script. Restricted containers may require
explicit loopback permission for live cases and make check-unit.

    make sanitize

The sanitizer target builds/runs checks with compiler-supported memory and
undefined-behavior instrumentation. Some restricted containers prevent
LeakSanitizer from tracing /proc. In that case an AddressSanitizer run with
ASAN_OPTIONS=detect_leaks=0 still checks invalid accesses, but is not a leak
check. Record that distinction in validation results.

EXTENDED FAILURE, STRESS, AND LONGEVITY TESTS

    make check-stress

This adds allocation-failure rollback checks, deterministic DCC/parser
stress, transport timeouts and storage faults/concurrent saves, and seeded
real-PTY editing/resize/modal stress. Test sockets use loopback only. The
descriptor-pressure test lowers only its child client's open-file limit
and checks that errors leave the interface usable and terminal restored.
The DCC stress duration defaults to 60 seconds and can be changed with
STRESS_SECONDS=90. These are development fixtures, with no runtime overhead
or new library dependency in the installed client.

    make soak SOAK_SECONDS=1200

This runs a bounded 20-minute soak with eight local verified-TLS servers,
20 channels per server, saturated retained history, incoming private
messages/mentions, live editing and resizing, and repeated secondary-server
reconnects. It checks PING and input responsiveness, preserved drafts, no
unintended messages, exact descriptor balance, resolver cleanup, bounded
resident memory growth, and terminal restoration. It prints JSON results;
progress goes to standard error. The Linux resource sampler verifies process
identity, including PID namespaces, before reading /proc. Unsupported or
unverifiable measurements fail clearly. This test needs Linux /proc and
is not a native BSD portability test.

    python3 tests/test_soak.py --seconds 1200 --servers 8 --tls > soak.json
    python3 tests/test_ui_stress.py --seed 20261027 --steps 1200 --rounds 48
    python3 tests/test_lifecycle_stress.py --sanitize

For UI checks against an instrumented build, set VIPIRC_BINARY to the
absolute build/sanitize/vipirc path after make sanitize. Record the actual
elapsed duration and executable hash. A bounded soak does not establish
months of uninterrupted reliability. The sanitizer build's RSS includes
instrumentation and must not be compared directly with optimized-build RSS.

OPTIONAL LOCAL PERFORMANCE MEASUREMENT

After an optimized build:

    make bench

This uses a real terminal PTY and a loopback fake server. It measures a
short idle interval, then receives 100000 synthetic messages across 20
conversation buffers with history_lines=5000. Ordered PING/PONG markers
verify progress through the stream; input probes and a final redraw check
that the client remains responsive. No public IRC server is contacted.

The JSON report identifies the exact binary hash, transport, workload,
timing, and measurement method. Linux uses /proc for the target process's
CPU and RSS after checking PPid, namespace PID, and executable identity;
other systems report the portable timing and child resource
usage available there. The local plain-IRC benchmark does not measure TLS
record overhead, long-duration reliability, or performance relative to
another client. During stress, the PTY is drained without interpreting
every terminal drawing sequence in the Python harness.

Direct use supports a different bounded workload:

    python3 tests/bench.py --messages 100000 --phases 4 --idle-seconds 3

To measure three independent clients idle at once, each with its own
loopback server, terminal PTY, and 20 joined channel views:

    python3 tests/bench.py --idle-instances 3

The report gives each process's measurements and their sum. Summed RSS
counts shared resident library pages in each process, so it is not an
exclusive physical-memory measurement. The extra idle clients exit before
the primary client's message stress phase.

MANUAL FROM THE SOURCE TREE

    ./vipirc --manual
    groff -Tascii -man doc/vipirc.1 | less

On a system with mandoc:

    mandoc doc/vipirc.1 | less

The same manual text is embedded for /help and /manual. Actions > Help and
original-keymap h open the same reader. No network access or external pager
is needed. Scroll with Up/Down, Page Up/Page Down, or Home/End. Escape,
Enter, q, or Back returns to the previous main view with its draft preserved;
Tab stays in the reader. Return first, then use Tab to focus chat input.

SOURCE LICENSE

Read LICENSE.md for the supplied Insha-Allah License Agreement. Dependencies
remain governed by their own licenses; see NOTICE.
