These instructions apply to the whole repository. Keep them concise and durable; update this file when build, test, release, architecture, or security expectations change.
sshoosh is a self-hosted SSH/TUI workspace chat for small teams and operators. Users connect with SSH keys and work inside a dense terminal UI for channels, threads, DMs, notifications, mentions, reactions, unread state, search, exports, and administration.
The runtime should stay small and operator-friendly: one Rust binary, one SQLite/libSQL database, one SSH host key, and optional app-level encryption.
src/cli/: clap CLI definitions, command dispatch, local dev helpers, and sensitive file writing.src/ssh/: SSH server/session transport, authentication handoff, render loop integration, and SSH-facing action formatting.src/app/: TUI application state, input handling, slash commands, navigation, rendering, theme, and terminal interaction behavior.src/client/:ClientSessionfacade used by the TUI/SSH layer to call feature behavior.src/features/: vertical product domains. Each domain owns its models, action handling, formatting, and durableServerStatebehavior where applicable. Shared cross-domain helpers live insrc/features/shared/;ServerStateand runtime live insrc/features/system/.src/db.rs: database connection, migrations, query wrapper, encryption, backups, doctor checks, master lease, and local file permissions.migrations/: SQL migrations embedded bysrc/db.rs.tests/integration.rs: end-to-end feature, persistence, SSH, auth, security, and CLI behavior coverage.docs/: Astro docs site. Usepnpm; do not editdocs/node_modules,docs/dist, ordocs/.astro.packaging/,Dockerfile,install.sh,.github/workflows/: release, install, systemd, Docker, Pages, and CI automation.
- Prefer existing module patterns over new abstractions. Keep edits narrowly scoped to the behavior being changed.
- Use
rg/rg --filesfor search. Exclude generated or ignored paths such astarget/,docs/node_modules/,docs/dist/, anddocs/.astro/. - Do not commit secrets, local databases, SSH host keys, release artifacts, or generated dependency directories.
- Protect
SSHOOSH_DB,SSHOOSH_DATABASE_AUTH_TOKEN,SSHOOSH_ENCRYPTION_KEY,SSHOOSH_SERVER_KEY, exports, backups, and provider snapshots as sensitive data. - Do not add a license file without an explicit owner decision.
- Keep user-facing commands, docs, CLI help, and TUI slash command references synchronized when behavior changes.
- Durable product behavior belongs in
ServerStateand the owning feature modules. Avoid putting persistence, permissions, audit, notification, or membership rules in render, input, SSH, or CLI code. src/appowns TUI state, rendering, input, slash command parsing/autocomplete, navigation, hit maps, and terminal affordances.src/sshshould remain transport/session glue: SSH auth, channels, render loop, terminal IO, and mapping TUI actions to client calls.src/clishould parse/administer commands and delegate behavior to feature/database APIs rather than duplicating business logic.ClientSessionis the app-facing facade over feature behavior. TUI actions should route through it instead of reaching directly intoServerState.- Database writes should go through the query wrapper in
src/db.rsso encryption, master/standby fencing, and local file protections remain effective. - Schema changes must update SQL migrations, the embedded migration list in
src/db.rs, and tests that prove upgrades and persistence still work.
- Unknown SSH keys must redeem a bootstrap or invite token via the SSH keyboard-interactive prompt before any account or key row is written. Tokens must never be parsed out of the SSH user field, sent over
auth_password, or stored in plaintext in logs. - The first activated account is bootstrapped with a one-time bootstrap token and becomes owner.
#generalis mandatory for activated users and must not become private, leaveable, archived, or deleted.- Public channels are discoverable, but content visibility and searchability depend on explicit membership.
- Private channel content, mentions, notifications, search results, and source links must stay visible only to members.
- Owner/admin boundaries matter. Do not allow admins to mint owners/admins or remove the last active owner.
- Admin and security-sensitive actions should be audited.
- Unread counts, notification read state, muted threads/DMs, saved messages, and source navigation should stay consistent after edits/deletes.
SSHOOSH_ENCRYPTION_KEYencrypts source content fields, butsearch_indexremains plaintext by design so full-text search works. Treat search data as sensitive at rest.- Local SQLite files, backups, generated bootstrap tokens, and server keys should use owner-only permissions where applicable.
- Preserve the dense, terminal-native operator UI. Avoid marketing-style copy, sparse layouts, or decorative UI.
- Use
src/app/theme.rsand existing render helpers for colors, panels, message cards, badges, selection, and status styles. - Keep rows compact and scannable. Text must truncate or wrap predictably at narrow terminal widths without shifting layout unexpectedly.
- Maintain keyboard and mouse behavior together. When changing an interaction, update hit maps, keyboard handling, mouse handling, and tests as needed.
- Keep visual states explicit: selected, focused, unread, muted, pinned, archived, private, saved, online/away/offline, and destructive confirmation states.
- Sanitize terminal-visible text and escape-sequence payloads; never let stored content inject terminal controls.
- Prefer deterministic rendering tests for layout and interaction changes.
- New or changed slash commands usually need coordinated updates in command specs, parsing, autocomplete/help, action handling, and tests.
- Keep command aliases documented in code and docs when they are user-facing.
- Commands that mutate durable state should surface clear errors and route through feature permissions.
- For edit/delete flows, keep ownership and confirmation behavior explicit in both keyboard and mouse paths.
- The project uses SQLite/libSQL.
SSHOOSH_DATABASE_URLoverridesSSHOOSH_DB; remote URLs requireSSHOOSH_DATABASE_AUTH_TOKEN. - Multi-node remote deployments rely on stable
SSHOOSH_NODE_IDvalues and master lease fencing. Do not bypass standby write protections. - For schema work, keep the baseline schema in
migrations/20260430000000_initial.sqlcoherent and add forward migrations when existing databases need an upgrade path. - When adding a migration file, add the
include_str!wiring and application order insrc/db.rs, plus tests for fresh and upgraded database behavior when relevant. - Keep FTS/search-index maintenance in step with create/edit/delete operations for threads, comments, and DMs.
Select the smallest relevant gate that proves the change. For PR-ready Rust changes, prefer just ci.
- Format check:
just format - Apply formatting:
just fmt - Lint:
just lint - Tests:
just test - Release build:
just build - Full Rust CI equivalent:
just ci - Docs build:
cd docs && pnpm install --frozen-lockfile && pnpm build
Focused checks are useful while iterating:
- CLI parsing:
cargo test cli::tests - Slash commands:
cargo test app::commands::tests - App/TUI behavior:
cargo test app::tests - Render helpers:
cargo test app::render::tests - Terminal escapes/sanitization:
cargo test terminal::tests - Feature/system unit behavior:
cargo test features::system::tests - Integration behavior:
cargo test --test integration - End-to-end SSH behavior:
cargo test --test integration ssh_e2e_authenticates_renders_and_creates_thread
Run docs checks when touching docs/, docs workflows, or public command documentation. Run release build or just ci before release-facing changes.
- Reloadable local server:
cargo run -- dev --host 127.0.0.1 --port 2222 - Auto-reconnecting local SSH client:
cargo run -- dev-ssh --host 127.0.0.1 --port 2222 - DB performance seed/bench helper:
cargo run -- dev-db-bench --users 50 --channels 50 --threads 1000 --comments 100000 --dms 5000
Useful local environment variables:
SSHOOSH_DB=./sshoosh.sqlite
SSHOOSH_SERVER_KEY=./sshoosh_server_ed25519
SSHOOSH_HOST=0.0.0.0
SSHOOSH_PORT=2222
SSHOOSH_NO_MOUSE=falseReleases are tag-driven. The release tag format is vX.Y.Z.
Before tagging:
- Update versioned metadata if needed, especially
Cargo.toml, docs, packaging text, and any release notes the owner asks for. - Confirm
Cargo.lockis current. - Run
just ci. - If docs changed, run
cd docs && pnpm install --frozen-lockfile && pnpm build. - Confirm the intended commit is on
mainand has the changes to release.
Tag and push:
git tag vX.Y.Z
git push origin vX.Y.ZWhat automation does after the tag push:
.github/workflows/release.ymlbuilds release binaries for Linux x86_64/aarch64, macOS x86_64/aarch64, and Windows x86_64.- The release workflow packages Unix targets as
.tar.gz, Windows as.zip, publishes GitHub Release assets, and uploadsSHA256SUMS.txt. - The release workflow then checks out
puemos/homebrew-tap, rendersFormula/sshoosh.rbfrompackaging/homebrew/sshoosh.rb.template, updates the tap README, commits, and pushes. This depends on theGH_TOKENsecret. .github/workflows/docker.ymlalso runs onv*.*.*tags and publishesghcr.io/puemos/sshooshimages for linux/amd64 and linux/arm64. Version tags also publishlatest.
After release:
- Verify the GitHub Release contains all expected artifacts and
SHA256SUMS.txt. - Verify installer download and checksum flow with
./install.sh --version vX.Y.Z --dir <tmp-dir>. - Verify the GHCR Docker image tags exist.
- Verify
puemos/homebrew-tapupdated andbrew install puemos/tap/sshooshresolves after Homebrew metadata catches up. - If release automation fails after publishing partial assets, inspect the failed workflow before retagging. Prefer fixing forward with the same tag only when it is safe and no public consumers have fetched broken artifacts.