Files
noise/AGENTS.md
T

4.0 KiB

Noise — Agent Instructions

What this is

Noise is a Signal Messenger client for Emacs, written in Elisp. It communicates with a local signal-cli instance via JSON-RPC over HTTP (default: localhost:8080). Conversations are stored in SQLite via emacs-sqlite3-api.

Technology & Architecture

Layer Technology
Language Emacs Lisp (Elisp)
Message backend signal-cli JSON-RPC (signal-cli daemon)
Real-time messages SSE via GET /api/v1/events
Local storage SQLite (emacs-sqlite3-api)
Runtime GNU Emacs
  • All Signal protocol work is delegated to signal-cli. Noise is a client — it does not implement Signal crypto.
  • The JSON-RPC interface is called over HTTP (not piped subprocess stdio), so requests are POST http://localhost:8080/api/v1/rpc with JSON-RPC 2.0 payloads.
  • Noise uses Server-Sent Events (SSE) to receive messages in real-time from signal-cli's /api/v1/events endpoint.
  • SQLite is the single source of truth for local state: contacts, conversations, messages, read/unread markers.
  • The address is configurable via the noise-signal-cli-address defcustom.
  • The Signal account is configurable via the noise-account defcustom (nil for a single-account daemon; required when signal-cli has multiple accounts registered). Account-scoped RPC calls go through noise-rpc-call-for-account.

User Experience Principles

These come directly from the UI mockup (signal-emacs-ui.html) and README:

  • Every action has a keybinding. There are no mouse targets, no toolbars, no sidebars.
  • Conversation switching is minibuffer completion (fuzzy narrowing, vertico-style). Press C-x b from anywhere to switch chats.
  • Compose inline at the prompt in a conversation buffer. RET sends; M-RET inserts a newline.
  • Chat list is an ibuffer-style root buffer (*signal*). n/p to move, RET to open a conversation.
  • Discoverability via which-key. The keymap is the menu — C-c s shows available bindings.
  • Evil compatibility out of the box. When evil is active, provide modal bindings for searching, creating conversations, sending, etc.
  • End-to-end encryption indicator (⌁ E2E) is always visible in the modeline.

File Naming & Conventions

  • Emacs Lisp source files live in the project root or a package directory and end in .el.
  • Package prefix is noise- for all symbols, functions, and variables.
  • Follow Emacs Lisp conventions: defcustom for user options, defvar for internal state, defun for commands.
  • Use ;;; as the comment starter for top-level headings, ;; for inline comments.

Key Dependencies

  • signal-cli — external program, must be installed and running (signal-cli daemon --receive-mode=manual)
  • emacs-sqlite3-api — Elisp package for SQLite (used for read/write on conversations, contacts, messages)

UI Screens (from mockup)

  1. Chat list (*signal*) — root buffer showing all conversations, unread counts, last message preview, timestamps
  2. Conversation buffer (*signal:<name>*) — full message history with inline compose prompt, day separators, delivery receipts
  3. Minibuffer switcher — fuzzy narrowing completion over all conversations, showing name, type (direct/group), unread count, last activity
  4. Which-key panel — discoverable keybindings menu triggered by prefix keys

Development Approach

  • Build iteratively — get the JSON-RPC bridge working first, then the SQLite schema, then the buffers.
  • Test with a real signal-cli daemon running locally. Verify registration/account linking before anything else.
  • Elisp files should be byte-compilable with emacs -batch -f batch-byte-compile.
  • Follow Emacs buffer-naming conventions and major-mode patterns.
  • Always respect noise-signal-cli-address as the configurable JSON-RPC endpoint.
  • Uses SSE (Server-Sent Events) for real-time message reception.
  • noise-sse.el handles connection and event parsing.
  • noise-receive.el processes envelopes and updates UI.
  • Automatic reconnection with exponential backoff.