Emacs

Decknix provides a modern, batteries-included Emacs experience with 13+ modules, background daemon, and three profile tiers.

Modules

ModuleDescriptionProfile
CoreModus theme, line numbers, better defaultsminimal+
CompletionVertico, Consult, Corfu, Embarkminimal+
EditingSmartparens, Crux, Move-text, EditorConfigminimal+
UIWhich-key, Helpful, Nerd-iconsminimal+
Undoundo-fu, vundo (visual undo tree)minimal+
ProjectProject management and navigationminimal+
WelcomeStartup screen with keybinding cheat sheetstandard+
DevelopmentFlycheck, Yasnippetstandard+
MagitGit interface, Forge (GitHub PRs), code-reviewstandard+
TreemacsProject file tree with git integrationstandard+
Languages30+ language modes with syntax highlightingstandard+
LSPEglot, kotlin-ls, jdt-ls, dape (debugging)full
Org-modeModern styling, presentations (Olivetti)full
HTTPREST client, jq integration, org-babelfull
Agent ShellAI agent interface (Augment Code)full

Key Bindings — Quick Reference

KeyAction
C-sSearch in buffer (consult-line)
C-x bSwitch buffer with preview
M-s rProject-wide ripgrep search
M-yBrowse kill ring
C-.Context actions (Embark)

Git (Magit)

KeyAction
C-x gMagit status
@ f fFetch forge topics (PRs/issues)
@ c pCreate pull request
@ l pList pull requests

File Tree (Treemacs)

KeyAction
C-x t tToggle treemacs
C-x t fFind current file in tree

LSP / Code

KeyAction
C-c l rRename symbol
C-c l aCode actions
C-c l fFormat region
C-c l FFormat buffer
C-c l dShow documentation

Debugging (dape)

KeyAction
C-c d dStart debugger
C-c d bToggle breakpoint
C-c d nStep over
C-c d sStep in
C-c d cContinue

Editing

KeyAction
C-aSmart home (Crux)
C-c dDuplicate line
M-up/downMove line/region
C-/Undo
C-?Redo
C-x uVisual undo tree (vundo)

Org-mode

KeyAction
F5 or C-c pStart/stop presentation
n / pNext/previous slide

Languages

30+ languages with syntax highlighting:

CategoryLanguages
PrimaryKotlin, Java, Scala, SQL, Terraform/HCL, Shell, Nix, Python
DataJSON, YAML, TOML, XML, Markdown
WebHTML, CSS/SCSS/LESS, JavaScript, TypeScript, JSX, Vue, Svelte

Emacs Daemon

Configured in modules/darwin/emacs.nix, enabled by default on macOS. Runs as a background launchd service — no Dock icon, no Cmd+Tab entry.

ec filename          # Open file in Emacs
ec -c -n             # New GUI frame
ec -c -n file.txt    # Open file in new GUI frame
ec -t file.txt       # Open in terminal
emacsclient -c       # Create new GUI frame

GUI frames appear in the Dock while open; closing a frame doesn't kill the daemon.

Opening and checking a cold daemon

nix-open emacs       # Wait for the launchd daemon to answer, then open a frame
nix-open -s emacs    # Quick status check; reports a busy/starting daemon promptly
nix-open -r emacs    # Explicitly restart the launchd daemon, wait, then open a frame

Plain nix-open emacs does not restart an already-running daemon. If launchd has not started it yet, it asks launchd to start it once. It prints elapsed readiness updates every five seconds, waits as long as startup takes, and can be cancelled with Ctrl-C without killing the daemon. --wait is redundant for Emacs; frame creation always waits for server readiness. Only -r kills the old daemon (losing unsaved in-memory buffers), so use it deliberately. It never opens a separate Emacs.app instance. A status probe is capped at two seconds; -s reports an initializing or busy daemon rather than hanging in emacsclient or calling it stopped.

On startup, surviving ACP brokers are reattached from their bounded event logs. Re-reading entire 50 MB or larger session transcripts on the Emacs main thread for each broker can leave the server socket unresponsive for minutes; a live broker already holds its model context, so automatic reattach skips that transcript scan and its continuation primer. The broker log restores visible recent turns. Historical M-p input-ring entries and the transcript-derived asking flag are not seeded on this fast automatic reattach path; a normal resume without a live broker still restores them from the transcript. Check the live conversation itself before relying on its asking badge after restart.

Because of that reattach, closing a session buffer is not the same as ending it. C-x k (and C-c s D) only detach: the agent keeps running and comes back on the next start. To end a session for good, discard it, which stops its broker and then kills the buffer:

Key / commandAction
k or a k on a sidebar rowDiscard that session
C-c s k (decknix-agent-session-discard)Discard this session, or pick several by name
C-k in the session picker (C-c s s)Discard the selected sessions (C-SPC marks several)
C-c s qQuit this session (also stops its broker)
a K on a sidebar rowKill the buffer's process only (the old k)

Restoring frames and windows after a restart

The daemon records every GUI frame, its tab-bar tabs and the windows in each tab when it exits (SIGTERM from launchctl kickstart -k runs kill-emacs-hook), just before the last frame is closed, and every five minutes. It records file, dired and magit buffers by path and agent-shell buffers by session id, in ~/.local/state/decknix/emacs-layout.el.

The first GUI frame after a restart asks once:

Restore saved layout (2 frames, 5 tabs, 3 sessions, saved 1m ago)? [y]es with sessions, [l]ayout only, [n]o

y rebuilds the frames and tabs and resumes the sessions through the same path as the sidebar's Previous restore. l puts a placeholder in each session window that resumes on RET. A file or directory that has gone shows *scratch* in its window, and a frame saved on a monitor that is no longer attached opens wherever macOS places it. Buffers that visit no file are reopened by their command when decknix-layout-recreate-alist knows one (by buffer name or major mode): by default the Review Board, the Session Board, every coordination board (*Board: NAME*, while its definition exists), the US Pilot board when installed, and *Messages*.

Key / commandAction
C-c D w (decknix-layout-save)Save the layout now
C-c D W (decknix-layout-restore)Restore it; C-u restores the run before the last

decknix-layout-restore-on-start chooses ask (default), notify, t (restore without asking) or nil. decknix-layout-restore-sessions set to nil makes a restore use placeholders instead of resuming. The first save of each run keeps the previous file as emacs-layout.el.prev, so quitting straight after a restart does not lose the layout worth restoring.

OptionDefaultDescription
services.emacs.decknix.enabletrueEnable Emacs daemon
services.emacs.decknix.packagepkgs.emacsEmacs package to use
services.emacs.decknix.additionalPath[]Extra PATH entries for daemon

Module Options Reference

Disabling Modules

{ ... }: {
  programs.emacs.decknix.enable = false;             # ALL emacs config
  programs.emacs.decknix.welcome.enable = false;     # Welcome screen
  programs.emacs.decknix.magit.enable = false;       # Git interface
  programs.emacs.decknix.magit.forge.enable = false;  # Just Forge
  programs.emacs.decknix.completion.enable = false;  # Completion stack
  programs.emacs.decknix.treemacs.enable = false;    # File tree
  programs.emacs.decknix.undo.enable = false;        # Undo enhancements
  programs.emacs.decknix.editing.enable = false;     # Editing enhancements
  programs.emacs.decknix.development.enable = false; # Flycheck/Yasnippet
  programs.emacs.decknix.ui.enable = false;          # UI enhancements
  programs.emacs.decknix.ui.icons.enable = false;    # Just icons
  programs.emacs.decknix.org.enable = false;         # Org enhancements
  programs.emacs.decknix.lsp.enable = false;         # LSP/IDE
  programs.emacs.decknix.http.enable = false;        # REST client
  programs.emacs.decknix.languages.enable = false;   # All languages
}

Customisation

{ pkgs, ... }: {
  # Add your own packages
  programs.emacs.extraPackages = epkgs: [ epkgs.evil epkgs.lsp-mode ];

  # Add your own config
  programs.emacs.extraConfig = ''
    (evil-mode 1)
    (setq my-custom-variable t)
  '';
}

Note: Evil mode (Vim emulation) is not included by default. Add it in your personal config as shown above.