Compare commits

...

37 Commits

Author SHA1 Message Date
liph 658498860e updated yazi 2026-05-12 23:21:51 +02:00
liph 794f26df04 updated dotfiles 2026-05-12 15:30:52 +02:00
liph 88e0293307 another commit 2026-04-18 09:34:10 +02:00
liph ceca3f7b4e changed atuin config 2026-03-19 20:38:22 +01:00
liph a1c09a6d3b updated zshrc for atuin 2026-03-19 20:15:34 +01:00
liph f12b027aa5 added opencode 2026-03-19 15:19:56 +01:00
liph a22912ce30 added atuin, changes in kanata, added wezterm 2026-03-18 20:51:43 +01:00
liph a5be19430e updated podman-update.sh 2026-02-25 10:58:58 +01:00
liph d5d9075ad2 added sudo to alias for podman-update.sh 2026-02-25 10:50:25 +01:00
liph 845ef5d076 updated podup scritp 2026-02-25 10:35:13 +01:00
liph 07d8051731 updated scripts (added podman-update.sh) 2026-02-25 10:26:28 +01:00
liph 1baf5ecddf updatged nvim-surround 2026-02-23 19:01:51 +01:00
liph 65658a34b8 updated alias 2026-02-23 17:43:24 +01:00
liph f901141e5f updated tmux config and plugins, added tmuxp to dotfiles 2026-02-23 17:37:56 +01:00
liph 8925d9677e added ansible script 2026-02-16 23:40:30 +01:00
liph e6e444fff7 updated yazi conf. nvim completions and latex 2026-02-13 09:37:55 +01:00
liph 84b025eb0e add username to ohmyposh config 2026-02-06 15:06:49 +01:00
liph e32afefde9 fixed the obsidian errors 2026-02-06 14:27:53 +01:00
liph f5ac9bce3e deleted the cmp-tabnine 2026-02-06 14:24:34 +01:00
liph ab69cc08f2 added funtioning mason.lua back 2026-02-06 10:44:38 +01:00
liph 7b590ef2ec deleted blink.lua but completed the completion.lua 2026-02-06 10:08:00 +01:00
liph b2194f79dd added hostname var in ohmyposh 2026-02-05 17:07:01 +01:00
liph 5a78d47050 fixed blink.lua 2026-02-05 16:37:16 +01:00
liph 001c74dfa7 fixed treesitter 2026-02-05 15:55:31 +01:00
liph 8b28075f40 fixed nvim setup? 2026-02-05 15:31:17 +01:00
liph 2e1ccf0cc2 added new configs with the dell comp 2026-02-05 13:46:21 +01:00
liph22 f3d1e7fdb1 added themes, niri waybar updat, nvim plugins updates and additions 2026-02-03 09:43:40 +01:00
liph22 1b652298ef updatet colorschemes 2026-01-19 21:05:12 +01:00
liph22 0ffe6606b6 changes in aerc, updated nvim plugins, yazi config, niri, changed hypr dotfiles folders 2026-01-15 09:33:02 +01:00
liph22 b9fe2697a0 added toggle-preview plugin in yazi, status bar with omp 2026-01-13 15:39:15 +01:00
liph22 5fc9e27e67 updated dotfiles 2026-01-13 09:28:51 +01:00
liph22 c4df37dacb added niri config with waybar, rip, nvim update, brightness sh 2026-01-12 22:26:10 +01:00
liph22 d7b808ce59 adde latex lsp and language 2026-01-10 14:22:02 +01:00
liph22 b7cfdb00a3 updated nvim 2026-01-08 22:04:23 +01:00
liph22 ce7e3a9824 updated yazi config, hyprland, tmux 2026-01-08 12:36:24 +01:00
liph22 70c6316f8e changes in whoosh 2026-01-07 15:00:47 +01:00
liph22 cdc1aa61c4 changes in whoosh 2026-01-07 15:00:35 +01:00
333 changed files with 38006 additions and 3285 deletions
+44
View File
@@ -0,0 +1,44 @@
# Agent Guidelines: Aerc Configuration Repository
This repository contains the configuration and customization for `aerc`, a terminal-based email client. It is optimized for a workflow involving `notmuch` for indexing, `mbsync` for synchronization, `abook` for address management, `khal` for calendar, and `taskwarrior` for tasks.
## 1. Build, Lint, and Test Commands
### Sync and Maintenance
- **Global Sync**: Run `mailsync` (bound to `u` in `aerc`) to perform:
1. `mbsync -a` (Mail sync)
2. `vdirsyncer sync` (Calendar sync)
3. `caldawarrior sync` (Task sync)
4. `notmuch new` for all account databases (Indexing)
5. `notmuch-abook-sync` (Address book population)
### Linting
- **Permissions**: `accounts.conf` must be `0600`.
- **Dependencies**: `w3m`, `bat`, `yazi`, `notmuch`, `abook`, `pass`, `isync`, `khal`, `vdirsyncer`, `task`, `taskwarrior-tui`, `caldawarrior`.
### Testing
- **Filters**: `cat email.html | w3m -T text/html -dump`
- **Calendar**: `khal list`
- **Tasks**: `task list`
## 2. Code Style Guidelines
### Aerc Configuration
- **Notmuch URI**: Use absolute triple-slash format: `source = notmuch:///home/liph/Mail/account_name`.
- **Credentials**: Use `pass` via `outgoing-cred-cmd = pass show mail/account_email`.
- **Sidebar**: Use separate `notmuch` databases per account directory for isolation.
### Keybindings
- **Leader**: `,` (comma).
- **Tab switching**: `Alt+n` (next) and `Alt+p` (prev).
- **Contacts**: `,ab` opens `abook`.
- **Calendar**: `,ca` opens unified `khal`.
- **Tasks**: `,ta` opens `taskwarrior-tui`.
### Terminal Tabs (Khal/Taskwarrior)
- **Escape**: Press `Ctrl+x` to release terminal focus and use `aerc` commands.
- **Tab Passthrough**: `Tab` and `Backtab` are passed to terminal apps for internal navigation.
### Neovim integration (`after/ftplugin/mail.lua`)
- **Completion**: Triggered by `Tab` on header lines using `abook --mutt-query`.
- **Fzf**: Triggered by `Ctrl+f` for interactive contact selection.
+19 -36
View File
@@ -1,50 +1,33 @@
[phil_live]
source = imaps://phil%40liphlink.xyz:Fqi5UAyr46e69fG@mail.liphlink.xyz:993
outgoing = smtp://phil%40liphlink.xyz:Fqi5UAyr46e69fG@mail.liphlink.xyz:587
default = INBOX
from = Phil <phil@liphlink.xyz>
copy-to = Sent
archive = Archive
postpone = Drafts
folders-sort = INBOX,Draftr,Sent,Archive,Spam,Trash
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
[phil_notmuch]
source = notmuch://~/Mail/phil
query-map = ~/.config/aerc/notmuch-queries.conf
[phil]
source = notmuch:///home/liph/Mail/phil
query-map = ~/.config/aerc/phil.queries
outgoing = smtp://phil%40liphlink.xyz@mail.liphlink.xyz:587
outgoing-cred-cmd = pass show mail/phil@liphlink.xyz
from = Phil <phil@liphlink.xyz>
default = Inbox
copy-to = Sent
postpone = Drafts
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
[spam_live]
source = imaps://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:993
outgoing = smtp://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:587
default = INBOX
from = Liph <spam@liphlink.xyz>
copy-to = Sent
archive = Archive
postpone = Drafts
folders-sort = INBOX,Drafts,Sent,Archive,Spam,Trash
aliases = blue@liphlink.xyz,red@liphlink.xyz
[spam_notmuch]
source = notmuch://~/Mail/spam
[spam]
source = notmuch:///home/liph/Mail/spam
query-map = ~/.config/aerc/notmuch-queries.conf
outgoing = smtp://spam%40liphlink.xyz@mail.liphlink.xyz:587
outgoing-cred-cmd = pass show mail/spam@liphlink.xyz
from = Liph <spam@liphlink.xyz>
copy-to = Sent
default = Inbox
aliases = blue@liphlink.xyz,red@liphlink.xyz
[proton]
source = imap+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1144
outgoing = smtp+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1026
default = INBOX
from = Liph <spam@liphlink.xyz>
copy-to = Sent
archive = Archive
postpone = Drafts
folders-sort = INBOX,Drafts,Sent,Archive,Spam,Trash
aliases = liiph@proton.me,liiph@pm.me,ph.waibel@proton.me,ph.waibel@pm.me
source = notmuch:///home/liph/Mail/proton
query-map = ~/.config/aerc/notmuch-queries.conf
outgoing = smtp+insecure://liiph%40protonmail.com@127.0.0.1:1025
outgoing-cred-cmd = pass show mail/liiph@protonmail.com
default = INBOX
from = Liph <liiph@proton.me>
copy-to = Sent
archive = Archive
default = Inbox
postpone = Drafts
aliases = liiph@proton.me,liiph@pm.me,ph.waibel@proton.me,ph.waibel@pm.me
+34 -38
View File
@@ -27,15 +27,9 @@ index-format=notmuch://~/.local/share/mail
# the user home dir. When redirecting aerc's output to a file using > shell
# redirection, this setting is ignored and log messages are printed to stdout.
#
#log-file=
log-file=~/.local/share/aerc/aerc.log
log-level=debug
# Only log messages above the specified level to log-file. Supported levels
# are: trace, debug, info, warn and error. When redirecting aerc's output to
# a file using > shell redirection, this setting is ignored and the log level
# is forced to trace.
#
# Default: info
#log-level=info
# Disable IPC entirely. Don't run commands (including mailto:... and mbox:...)
# in an existing aerc instance, and don't start an IPC server to allow
@@ -95,10 +89,12 @@ index-format=notmuch://~/.local/share/mail
# width specifier is set, '*' is used by default.
#
# Default: flags:4,name<20%,subject,date>=
#index-columns=flags:4,name<20%,subject,date>=
index-columns=flags:4,name<35%,subject,date>=
column-name={{if match .Folder "(?i)Sent|Gesendet"}}{{index (.To | persons) 0}}{{else}}{{index (.From | persons) 0}}{{end}}
border-char-vertical="│"
border-char-horizontal="─"
styleset-name=catppuccin-mocha
styleset-name="gruvbox-dark"
threading-enabled=true
#
# Each name in index-columns must have a corresponding column-$name setting.
@@ -181,7 +177,7 @@ styleset-name=catppuccin-mocha
# Width of the sidebar, including the border.
#
# Default: 22
sidebar-width=22
sidebar-width=34
#
# Default split layout for message list tabs. The syntax is:
@@ -251,13 +247,13 @@ mouse-enabled=false
# See aerc-templates(7) for all available fields and functions.
#
# Default: {{.Folder}}
#dirlist-left={{.Folder}}
dirlist-left={{if match .Folder "/"}}{{.Style (index (.Folder | split "/" | tail 1) 0) "folder_sub"}}{{else}}{{.Folder}}{{end}}
# Template for the right side of the directory list.
# See aerc-templates(7) for all available fields and functions.
#
# Default: {{if .Unread}}{{humanReadable .Unread}}{{end}}
#dirlist-right={{if .Unread}}{{humanReadable .Unread}}{{end}}
dirlist-right={{if .Unread}}{{humanReadable .Unread}} / {{end}}{{humanReadable .Exists}}
# Delay after which the messages are actually listed when entering a directory.
# This avoids loading messages when skipping over folders and makes the UI more
@@ -270,13 +266,13 @@ mouse-enabled=false
# expand the folders.
#
# Default: false
#dirlist-tree=false
dirlist-tree=true
# If dirlist-tree is enabled, set level at which folders are collapsed by
# default. Set to 0 to disable.
#
# Default: 0
#dirlist-collapse=0
dirlist-collapse=1
# List of space-separated criteria to sort the messages by, see *sort*
# command in *aerc*(1) for reference. Prefixing a criterion with "-r "
@@ -576,11 +572,11 @@ sort = -r date
[viewer]
# pager=less -Rc
pager=bat
html-unsafe-images=false
html-filter=dante
pager= bat --style=plain --paging=always
# html-unsafe-images=false
# html-filter=dante
image/*=chafa -f kitty -s ${width}x${height}
text/html=dante
text/html=w3m -T text/html -dump -o display_link_number=1
application/pdf=zathura
video/*=mpv
header-layout=From,To,Subject,Date
@@ -637,9 +633,16 @@ header-layout=From,To,Subject,Date
#parse-http-links=true
[compose]
editor=nvim # or your preferred editor
editor=nvim +'set ft=mail.markdown'
# editor=nvim +setf\ mail
# editor=nvim # or your preferred editor
# Address book command
address-book-cmd=abook --mutt-query '%s'
# Specifies the command to run the editor with. It will be shown in an embedded
# Auto-complete addresses
# address-book-cmd-match='%s'
# address-book-cmd=abook --mutt-query '%s'
# terminal, though it may also launch a graphical window if the environment
# supports it. Defaults to $EDITOR, or vi.
#editor=
@@ -697,7 +700,7 @@ edit-headers=true
# standard output, one file per line. If it is present, then aerc does not
# capture the standard output and instead reads the files from the temporary
# file which should have the same format.
#file-picker-cmd=
#file-picker-cmd=/home/liph/.local/bin/yazi-picker %f
#
# Allow to address yourself when replying
@@ -729,18 +732,10 @@ edit-headers=true
# text. To actually make use of this format's features, you'll need support in
# your editor.
#
#format-flowed=false
format-flowed=false
[multipart-converters]
#
# Converters allow to generate multipart/alternative messages by converting the
# main text/plain part into any other MIME type. Only exact MIME types are
# accepted. The commands are invoked with sh -c and are expected to output
# valid UTF-8 text.
#
# Example (obviously, this requires that you write your main text/plain body
# using the markdown syntax):
#text/html=pandoc -f markdown -t html --standalone
text/html=/home/liph/.local/bin/aerc-md-convert
[filters]
#
@@ -782,9 +777,7 @@ text/plain=colorize
text/calendar=calendar
message/delivery-status=colorize
message/rfc822=colorize
#text/html=pandoc -f html -t plain | colorize
text/html=! html
text/html=! w3m -T text/html -I UTF-8
text/html=w3m -T text/html -dump -o display_link_number=1
#text/*=bat -fP --file-name="$AERC_FILENAME"
#application/x-sh=bat -fP -l sh
#image/*=catimg -w $(tput cols) -
@@ -818,12 +811,12 @@ text/html=! w3m -T text/html -I UTF-8
# text/html=surf -dfgms
# text/plain=gvim {} +125
# message/rfc822=thunderbird
[hooks]
# text/html=librewolf
text/html=w3m
x-scheme-handler/http=librewolf
x-scheme-handler/https=librewolf
text/calendar=khal import --batch
[hooks]
#
# Hooks are triggered whenever the associated event occurs.
@@ -888,3 +881,6 @@ template-dirs=${XDG_CONFIG_HOME:-~/.config}/aerc/templates
#
# default: forward_as_body
#forwards=forward_as_body
[terminal]
passthrough=false
+883
View File
@@ -0,0 +1,883 @@
#
# aerc main configuration
[general]
# Used as a default path for save operations if no other path is specified.
# ~ is expanded to the current user home dir.
#
#default-save-path=
# If set to "gpg", aerc will use system gpg binary and keystore for all crypto
# operations. If set to "internal", the internal openpgp keyring will be used.
# If set to "auto", the system gpg will be preferred unless the internal
# keyring already exists, in which case the latter will be used.
#
# Default: auto
#pgp-provider=auto
# By default, the file permissions of accounts.conf must be restrictive and
# only allow reading by the file owner (0600). Set this option to true to
# ignore this permission check. Use this with care as it may expose your
# credentials.
#
# Default: false
unsafe-accounts-conf=false
index-format=notmuch://~/.local/share/mail
# Output log messages to specified file. A path starting with ~/ is expanded to
# the user home dir. When redirecting aerc's output to a file using > shell
# redirection, this setting is ignored and log messages are printed to stdout.
#
log-file=~/.local/share/aerc/aerc.log
log-level=debug
# Disable IPC entirely. Don't run commands (including mailto:... and mbox:...)
# in an existing aerc instance, and don't start an IPC server to allow
# subsequent aerc instances to run commands in the current one.
#
# Default: false
#disable-ipc=false
# Don't run mailto:... commands over IPC; start a new aerc instance with the
# composer instead.
#
# Default: false
#disable-ipc-mailto=false
#
# Don't run mbox:... commands over IPC; start a new aerc instance with the mbox
# file instead.
#
# Default: false
#disable-ipc-mbox=false
# Set the $TERM environment variable used for the embedded terminal.
#
# Default: xterm-256color
#term=xterm-256color
# Display OSC8 strings in the embedded terminal
#
# Default: false
#enable-osc8=false
# Default shell command to use for :menu. This will be executed with sh -c and
# will run in an popover dialog.
#
# Any occurrence of %f will be replaced by a temporary file path where the
# command is expected to write output lines to be consumed by :menu. Otherwise,
# the lines will be read from the command's standard output.
#
# Examples:
# default-menu-cmd=fzf
# default-menu-cmd=fzf --multi
# default-menu-cmd=dmenu -l 20
# default-menu-cmd=ranger --choosefiles=%f
#
#default-menu-cmd=
[ui]
#
# Describes the format for each row in a mailbox view. This is a comma
# separated list of column names with an optional align and width suffix. After
# the column name, one of the '<' (left), ':' (center) or '>' (right) alignment
# characters can be added (by default, left) followed by an optional width
# specifier. The width is either an integer representing a fixed number of
# characters, or a percentage between 1% and 99% representing a fraction of the
# terminal width. It can also be one of the '*' (auto) or '=' (fit) special
# width specifiers. Auto width columns will be equally attributed the remaining
# terminal width. Fit width columns take the width of their contents. If no
# width specifier is set, '*' is used by default.
#
# Default: flags:4,name<20%,subject,date>=
index-columns=flags:4,name<35%,subject,date>=
column-name={{if match .Folder "(?i)Sent|Gesendet"}}{{index (.To | persons) 0}}{{else}}{{index (.From | persons) 0}}{{end}}
border-char-vertical="│"
border-char-horizontal="─"
styleset-name="gruvbox-dark"
threading-enabled=true
#
# Each name in index-columns must have a corresponding column-$name setting.
# All column-$name settings accept golang text/template syntax. See
# aerc-templates(7) for available template attributes and functions.
#
# Here are some examples to show the To field instead of the From field for
# an email (modifying column-name):
#
# 1. a generic one
# column-name={{ .Peer | names | join ", " }}
# 2. based upon the selected folder
# column-name={{if match .Folder "^(Gesendet|Sent)$"}}{{index (.To | names) 0}}{{else}}{{index (.From | names) 0}}{{end}}
#
# Default settings
#column-flags={{.Flags | join ""}}
#column-name={{index (.From | names) 0}}
#column-subject={{.ThreadPrefix}}{{.Subject}}
#column-date={{.DateAutoFormat .Date.Local}}
#
# String separator inserted between columns. When the column width specifier is
# an exact number of characters, the separator is added to it (i.e. the exact
# width will be fully available for the column contents).
#
# Default: " "
#column-separator=" "
#
# See time.Time#Format at https://godoc.org/time#Time.Format
#
# Default: 2006 Jan 02
#timestamp-format=2006 Jan 02
#
# Index-only time format for messages that were received/sent today.
# If this is empty, timestamp-format is used instead.
#
# Default: 15:04
#this-day-time-format=15:04
#
# Index-only time format for messages that were received/sent within the last
# 7 days. If this is empty, timestamp-format is used instead.
#
# Default: Jan 02
#this-week-time-format=Jan 02
#
# Index-only time format for messages that were received/sent this year.
# If this is empty, timestamp-format is used instead.
#
#Default: Jan 02
#this-year-time-format=Jan 02
#
# Overrides timestamp-format for the message view.
#
# Default: 2006 Jan 02, 15:04 GMT-0700
#message-view-timestamp-format=2006 Jan 02, 15:04 GMT-0700
#
# If set, overrides timestamp-format in the message view for messages
# that were received/sent today.
#
#message-view-this-day-time-format=
# If set, overrides timestamp-format in the message view for messages
# that were received/sent within the last 7 days.
#
#message-view-this-week-time-format=
#
# If set, overrides *timestamp-format* in the message view for messages
# that were received/sent this year.
#
#message-view-this-year-time-format=
#
# Width of the sidebar, including the border.
#
# Default: 22
sidebar-width=22
#
# Default split layout for message list tabs. The syntax is:
#
# [<direction>] <size>
#
# <direction> is optional and defaults to horizontal. It can take one
# of the following values: h, horiz, horizontal, v, vert, vertical.
#
# <size> is a positive integer representing the size (in terminal cells)
# of the message list window.
#
#message-list-split=
#
# Message to display when viewing an empty folder.
#
# Default: (no messages)
#empty-message=(no messages)
# Message to display when no folders exists or are all filtered
#
# Default: (no folders)
#empty-dirlist=(no folders)
#
# Value to set {{.Subject}} template to when subject is empty.
#
# Default: (no subject)
#empty-subject=(no subject)
# Enable mouse events in the ui, e.g. clicking and scrolling with the mousewheel
#
# Default: false
mouse-enabled=false
#
# Ring the bell when new messages are received
#
# Default: true
#new-message-bell=true
#
# Template to use for Account tab titles
#
# Default: {{.Account}}
#tab-title-account={{.Account}}
#
# Template to use for Composer tab titles
#
# Default: {{if .To}}to:{{index (.To | shortmboxes) 0}} {{end}}{{.SubjectBase}}
#tab-title-composer={{if .To}}to:{{index (.To | shortmboxes) 0}} {{end}}{{.SubjectBase}}
#
# Template to use for Message Viewer tab titles
#
# Default: {{.Subject}}
#tab-title-viewer={{.Subject}}
# Marker to show before a pinned tab's name.
#
# Default: `
#pinned-tab-marker='`'
# Template for the left side of the directory list.
# See aerc-templates(7) for all available fields and functions.
#
# Default: {{.Folder}}
#dirlist-left={{.Folder}}
# Template for the right side of the directory list.
# See aerc-templates(7) for all available fields and functions.
#
# Default: {{if .Unread}}{{humanReadable .Unread}}{{end}}
#dirlist-right={{if .Unread}}{{humanReadable .Unread}}{{end}}
# Delay after which the messages are actually listed when entering a directory.
# This avoids loading messages when skipping over folders and makes the UI more
# responsive. If you do not want that, set it to 0s.
#
# Default: 200ms
#dirlist-delay=200ms
# Display the directory list as a foldable tree that allows to collapse and
# expand the folders.
#
# Default: false
dirlist-tree=true
# If dirlist-tree is enabled, set level at which folders are collapsed by
# default. Set to 0 to disable.
#
# Default: 0
dirlist-collapse=1
# List of space-separated criteria to sort the messages by, see *sort*
# command in *aerc*(1) for reference. Prefixing a criterion with "-r "
# reverses that criterion.
#
# Example: "from -r date"
#
sort = -r date
# Moves to next message when the current message is deleted
#
# Default: true
#next-message-on-delete=true
# Automatically set the "seen" flag when a message is opened in the message
# viewer.
#
# Default: true
#auto-mark-read=true
# The directories where the stylesets are stored. It takes a colon-separated
# list of directories. If this is unset or if a styleset cannot be found, the
# following paths will be used as a fallback in that order:
#
# ${XDG_CONFIG_HOME:-~/.config}/aerc/stylesets
# ${XDG_DATA_HOME:-~/.local/share}/aerc/stylesets
# /usr/local/share/aerc/stylesets
# /usr/share/aerc/stylesets
#
#stylesets-dirs=
# Uncomment to use box-drawing characters for vertical and horizontal borders.
#
# Default: "│" and "─"
#border-char-vertical="│"
#border-char-horizontal="─"
# Sets the styleset to use for the aerc ui elements.
#
# Default: default
#styleset-name=default
# Activates fuzzy search in commands and their arguments: the typed string is
# searched in the command or option in any position, and need not be
# consecutive characters in the command or option.
#
# Default: false
#fuzzy-complete=false
# How long to wait after the last input before auto-completion is triggered.
#
# Default: 250ms
#completion-delay=250ms
# The minimum required characters to allow auto-completion to be triggered after
# completion-delay.
#
# Setting this to "manual" disables automatic completion, leaving only the
# manually triggered completion with the $complete key (see aerc-binds(5) for
# more details).
#
# Default: 1
#completion-min-chars=1
#
# Global switch for completion popovers
#
# Default: true
#completion-popovers=true
# Uncomment to use UTF-8 symbols to indicate PGP status of messages
#
# Default: ASCII
#icon-unencrypted=
#icon-encrypted=✔
#icon-signed=✔
#icon-signed-encrypted=✔
#icon-unknown=✘
#icon-invalid=⚠
# Reverses the order of the message list. By default, the message list is
# ordered with the newest (highest UID) message on top. Reversing the order
# will put the oldest (lowest UID) message on top. This can be useful in cases
# where the backend does not support sorting.
#
# Default: false
#reverse-msglist-order = false
# Reverse display of the message threads. Default order is the initial
# message is on the top with all the replies being displayed below. The
# reverse option will put the initial message at the bottom with the
# replies on top.
#
# Default: false
#reverse-thread-order=false
# Positions the cursor on the last message in the message list (at the
# bottom of the view) when opening a new folder.
#
# Default: false
#select-last-message=false
# Sort the thread siblings according to the sort criteria for the messages. If
# sort-thread-siblings is false, the thread siblings will be sorted based on
# the message UID in ascending order. This option is only applicable for
# client-side threading with a backend that enables sorting. Note that there's
# a performance impact when sorting is activated.
#
# Default: false
#sort-thread-siblings=false
# Set the scroll offset in number of lines from the top and bottom of the
# message list.
#
# Default: 0
#msglist-scroll-offset = 0
#
# Enable a threaded view of messages. If this is not supported by the backend
# (IMAP server or notmuch), threads will be built by the client.
#
# Default: false
#threading-enabled=false
# Force client-side thread building
#
# Default: false
#force-client-threads=false
# If no References nor In-Reply-To headers can be matched to build client side
# threads, fallback to similar subjects.
#
# Default: false
#threading-by-subject=false
# Show thread context enables messages which do not match the current query (or
# belong to the current mailbox) to be shown for context. These messages can be
# styled separately using "msglist_thread_context" in a styleset. This feature
# is not supported by all backends
#
# Default: false
#show-thread-context=false
# Debounce client-side thread building
#
# Default: 50ms
#client-threads-delay=50ms
#
# Thread prefix customization:
#
# Customize the thread prefix appearance by selecting the arrow head.
#
# Default: ">"
#thread-prefix-tip = ">"
#
# Customize the thread prefix appearance by selecting the arrow indentation.
#
# Default: " "
#thread-prefix-indent = " "
#
# Customize the thread prefix appearance by selecting the vertical extension of
# the arrow.
#
# Default: "│"
#thread-prefix-stem = "│"
#
# Customize the thread prefix appearance by selecting the horizontal extension
# of the arrow.
#
# Default: ""
#thread-prefix-limb = ""
#
# Customize the thread prefix appearance by selecting the folded thread
# indicator.
#
# Default: "+"
#thread-prefix-folded = "+"
#
# Customize the thread prefix appearance by selecting the unfolded thread
# indicator.
#
# Default: ""
#thread-prefix-unfolded = ""
#
# Customize the thread prefix appearance by selecting the first child connector.
#
# Default: ""
#thread-prefix-first-child = ""
#
# Customize the thread prefix appearance by selecting the connector used if
# the message has siblings.
#
# Default: "├─"
#thread-prefix-has-siblings = "├─"
#
# Customize the thread prefix appearance by selecting the connector used if the
# message has no parents and no children.
#
# Default: ""
#thread-prefix-lone = ""
#
# Customize the thread prefix appearance by selecting the connector used if the
# message has no parents and has children.
#
# Default: ""
#thread-prefix-orphan = ""
#
# Customize the thread prefix appearance by selecting the connector for the last
# sibling.
#
# Default: "└─"
#thread-prefix-last-sibling = "└─"
#
# Customize the reversed thread prefix appearance by selecting the connector for
# the last sibling.
#
# Default: "┌─"
#thread-prefix-last-sibling-reverse = "┌─"
#
# Customize the thread prefix appearance by selecting the connector for dummy
# thread.
#
# Default: "┬─"
#thread-prefix-dummy = "┬─"
#
# Customize the reversed thread prefix appearance by selecting the connector for
# dummy thread.
#
# Default: "┴─"
#thread-prefix-dummy-reverse = "┴─"
#
# Customize the reversed thread prefix appearance by selecting the first child
# connector.
#
# Default: ""
#thread-prefix-first-child-reverse = ""
#
# Customize the reversed thread prefix appearance by selecting the connector
# used if the message has no parents and has children.
#
# Default: ""
#thread-prefix-orphan-reverse = ""
[statusline]
#
# Describes the format for the status line. This is a comma separated list of
# column names with an optional align and width suffix. See [ui].index-columns
# for more details. To completely mute the status line except for push
# notifications, explicitly set status-columns to an empty string.
#
# Default: left<*,center:=,right>*
#status-columns=left<*,center:=,right>*
#
# Each name in status-columns must have a corresponding column-$name setting.
# All column-$name settings accept golang text/template syntax. See
# aerc-templates(7) for available template attributes and functions.
#
# Default settings
#column-left=[{{.Account}}] {{.StatusInfo}}
#column-center={{.PendingKeys}}
#column-right={{.TrayInfo}} | {{cwd}}
#
# String separator inserted between columns.
# See [ui].column-separator for more details.
#
#column-separator=" "
# Specifies the separator between grouped statusline elements.
#
# Default: " | "
#separator=" | "
# Defines the mode for displaying the status elements.
# Options: text, icon
#
# Default: text
#display-mode=text
[viewer]
# pager=less -Rc
pager= bat --style=plain --paging=always
# html-unsafe-images=false
# html-filter=dante
image/*=chafa -f kitty -s ${width}x${height}
text/html=w3m -T text/html -dump -o display_link_number=1
application/pdf=zathura
video/*=mpv
header-layout=From,To,Subject,Date
#w3m -dump -T text/html -o display_link_number=1
#
# Specifies the pager to use when displaying emails. Note that some filters
# may add ANSI codes to add color to rendered emails, so you may want to use a
# pager which supports ANSI codes.
#
# Default: less -Rc
#
# If an email offers several versions (multipart), you can configure which
# mimetype to prefer. For example, this can be used to prefer plaintext over
# html emails.
#
# Default: text/plain,text/html
#alternatives=text/plain,text/html
#
# Default setting to determine whether to show full headers or only parsed
# ones in message viewer.
#
# Default: false
#show-headers=false
#
# Layout of headers when viewing a message. To display multiple headers in the
# same row, separate them with a pipe, e.g. "From|To". Rows will be hidden if
# none of their specified headers are present in the message.
#
# Default: From|To,Cc|Bcc,Date,Subject
#header-layout=From|To,Cc|Bcc,Date,Subject
# Whether to always show the mimetype of an email, even when it is just a single part
#
# Default: false
#always-show-mime=false
# Define the maximum height of the mimetype switcher before a scrollbar is used.
# The height of the mimetype switcher is restricted to half of the display
# height. If the provided value for the height is zero, the number of parts will
# be used as the height of the type switcher.
#
# Default: 0
#max-mime-height = 0
# Parses and extracts http links when viewing a message. Links can then be
# accessed with the open-link command.
#
# Default: true
#parse-http-links=true
[compose]
editor=nvim +'set ft=mail.markdown'
# editor=nvim +setf\ mail
# editor=nvim # or your preferred editor
# Address book command
address-book-cmd=abook --mutt-query '%s'
# Auto-complete addresses
# address-book-cmd-match='%s'
# address-book-cmd=abook --mutt-query '%s'
# terminal, though it may also launch a graphical window if the environment
# supports it. Defaults to $EDITOR, or vi.
#editor=
#
# When set, aerc will create and read .eml files for composing that have
# non-standard \n linebreaks. This is only relevant if the used editor does not
# support CRLF linebreaks.
#
#lf-editor=false
#
# Default header fields to display when composing a message. To display
# multiple headers in the same row, separate them with a pipe, e.g. "To|From".
#
# Default: To|From,Subject
#header-layout=To|From,Subject
#
# Edit headers into the text editor instead than separate fields.
#
# When this is true, address-book-cmd is not supported and address completion
# is left to the editor itself. Also, displaying multiple headers on the same
# line is not possible.
#
# Default: false
edit-headers=true
#
# Sets focus to the email body when the composer window opens.
#
# Default: false
#focus-body=false
#
# Specifies the command to be used to tab-complete email addresses. Any
# occurrence of "%s" in the address-book-cmd will be replaced with what the
# user has typed so far.
#
# The command must output the completions to standard output, one completion
# per line. Each line must be tab-delimited, with an email address occurring as
# the first field. Only the email address field is required. The second field,
# if present, will be treated as the contact name. Additional fields are
# ignored.
#
# This parameter can also be set per account in accounts.conf.
#address-book-cmd=
# Specifies the command to be used to select attachments. Any occurrence of
# '%s' in the file-picker-cmd will be replaced with the argument <arg>
# to :attach -m <arg>. Any occurrence of '%f' will be replaced by the
# location of a temporary file, from which aerc will read the selected files.
#
# If '%f' is not present, the command must output the selected files to
# standard output, one file per line. If it is present, then aerc does not
# capture the standard output and instead reads the files from the temporary
# file which should have the same format.
#file-picker-cmd=/home/liph/.local/bin/yazi-picker %f
#
# Allow to address yourself when replying
#
# Default: true
#reply-to-self=true
# Warn before sending an email with an empty subject.
#
# Default: false
#empty-subject-warning=false
#
# Warn before sending an email that matches the specified regexp but does not
# have any attachments. Leave empty to disable this feature.
#
# Uses Go's regexp syntax, documented at https://golang.org/s/re2syntax. The
# "(?im)" flags are set by default (case-insensitive and multi-line).
#
# Example:
# no-attachment-warning=^[^>]*attach(ed|ment)
#
#no-attachment-warning=
#
# When set, aerc will generate "format=flowed" bodies with a content type of
# "text/plain; format=flowed" as described in RFC3676. This format is easier to
# handle for some mailing software, and generally just looks like ordinary
# text. To actually make use of this format's features, you'll need support in
# your editor.
#
format-flowed=false
[multipart-converters]
text/html=/home/liph/.local/bin/aerc-md-convert
[filters]
#
text/plain=sed 's/^$//'
# Filters allow you to pipe an email body through a shell command to render
# certain emails differently, e.g. highlighting them with ANSI escape codes.
#
# The commands are invoked with sh -c. The following folders are prepended to
# the system $PATH to allow referencing filters from their name only:
#
# ${XDG_CONFIG_HOME:-~/.config}/aerc/filters
# ~/.local/libexec/aerc/filters
# ${XDG_DATA_HOME:-~/.local/share}/aerc/filters
# $PREFIX/libexec/aerc/filters
# $PREFIX/share/aerc/filters
# /usr/libexec/aerc/filters
# /usr/share/aerc/filters
#
# If you want to run a program in your default $PATH which has the same name
# as a builtin filter (e.g. /usr/bin/colorize), use its absolute path.
#
# The following variables are defined in the filter command environment:
#
# AERC_MIME_TYPE the part MIME type/subtype
# AERC_FORMAT the part content type format= parameter
# AERC_FILENAME the attachment filename (if any)
# AERC_SUBJECT the message Subject header value
# AERC_FROM the message From header value
#
# The first filter which matches the email's mimetype will be used, so order
# them from most to least specific.
#
# You can also match on non-mimetypes, by prefixing with the header to match
# against (non-case-sensitive) and a comma, e.g. subject,text will match a
# subject which contains "text". Use header,~regex to match against a regex.
#
text/plain=colorize
text/calendar=calendar
message/delivery-status=colorize
message/rfc822=colorize
text/html=w3m -T text/html -dump -o display_link_number=1
#text/*=bat -fP --file-name="$AERC_FILENAME"
#application/x-sh=bat -fP -l sh
#image/*=catimg -w $(tput cols) -
#subject,~Git(hub|lab)=lolcat -f
#from,thatguywhodoesnothardwraphismessages=wrap -w 100 | colorize
# This special filter is only used to post-process email headers when
# [viewer].show-headers=true
# By default, headers are piped directly into the pager.
#
.headers=colorize
[openers]
#
# Openers allow you to specify the command to use for the :open and :open-link
# actions on a per-MIME-type basis. The :open-link URL scheme is used to
# determine the MIME type as follows: x-scheme-handler/<scheme>.
#
# {} is expanded as the temporary filename or URL to be opened with proper
# shell quoting. If it is not encountered in the command, the filename/URL will
# be appended to the end of the command. The command will then be executed with
# `sh -c`.
#
# Like [filters], openers support basic shell globbing. The first opener which
# matches the part's MIME type (or URL scheme handler MIME type) will be used,
# so order them from most to least specific.
#
# Examples:
# x-scheme-handler/irc=hexchat
# x-scheme-handler/http*=printf '%s' {} | wl-copy
# text/html=surf -dfgms
# text/plain=gvim {} +125
# message/rfc822=thunderbird
text/html=w3m
x-scheme-handler/http=librewolf
x-scheme-handler/https=librewolf
text/calendar=khal import --batch
[hooks]
#
# Hooks are triggered whenever the associated event occurs.
#
# Executed when a new email arrives in the selected folder
#mail-received=notify-send "[$AERC_ACCOUNT/$AERC_FOLDER] New mail from $AERC_FROM_NAME" "$AERC_SUBJECT"
#
# Executed when mail is deleted from a folder
#mail-deleted=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &
#
# Executed when aerc adds mail to a folder
#mail-added=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &
#
# Executed when aerc starts
#aerc-startup=aerc :terminal calcurse && aerc :next-tab
#
# Executed when aerc shuts down.
#aerc-shutdown=
#
# Executed when notmuch tags are modified.
#tag-modified=
#
# Executed when flags are changed on a message.
#flag-changed=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &
[templates]
# Templates are used to populate email bodies automatically.
###
new-message=new-msg
quoted-reply=reply-quoted
# The directories where the templates are stored. It takes a colon-separated
# list of directories. If this is unset or if a template cannot be found, the
# following paths will be used as a fallback in that order:
#
# ${XDG_CONFIG_HOME:-~/.config}/aerc/templates
# ${XDG_DATA_HOME:-~/.local/share}/aerc/templates
# /usr/local/share/aerc/templates
# /usr/share/aerc/templates
#
template-dirs=${XDG_CONFIG_HOME:-~/.config}/aerc/templates
# The default template to be used for new messages.
#
# default: new_message
#new-message=new_message
# The default template to be used for quoted replies.
#
# default: quoted_reply
#quoted-reply=quoted_reply
# The default template to be used for forward as body.
#
# default: forward_as_body
#forwards=forward_as_body
+89 -171
View File
@@ -1,177 +1,115 @@
# Binds are of the form <key sequence> = <command to run>
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit
<C-p> = :prev-tab<Enter>
<C-PgUp> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
<C-PgDn> = :next-tab<Enter>
\[t = :prev-tab<Enter>
\]t = :next-tab<Enter>
<C-t> = :term<Enter>
# GLOBAL Navigation & Contacts
( = :term abook<Enter>
,ab = :term abook<Enter>
,ca = :term khal interactive<Enter>
,c1 = :term khal interactive -a personal<Enter>
,c2 = :term khal interactive -a healplaylove<Enter>
,c3 = :term khal interactive -a glarisegg<Enter>
,ta = :term taskwarrior-tui<Enter>
<A-p> = :prev-tab<Enter>
<A-n> = :next-tab<Enter>
<C-t> = :terminal<Enter>
? = :help keys<Enter>
<C-c> = :prompt 'Quit?' quit<Enter>
<C-q> = :prompt 'Quit?' quit<Enter>
<C-z> = :suspend<Enter>
[messages]
# Switch between Adresses
,1 = :change-tab phil_live<Enter>
,2 = :change-tab phil_notmuch<Enter>
,3 = :change-tab spam_live<Enter>
,4 = :change-tab spam_notmuch<Enter>
,5 = :change-tab proton<Enter>
# Sync
u = :term /home/liph/.local/bin/mailsync<Enter>
# d = :modify -inbox +deleted<Enter>
# a = :modify -inbox<Enter>
# # Delete = tag as deleted and remove from inbox
# d = :tag +deleted -inbox<Enter>
# Navigation
j = :next<Enter>
k = :prev<Enter>
<Down> = :next<Enter>
<Up> = :prev<Enter>
g = :select 0<Enter>
G = :select -1<Enter>
J = :next-folder<Enter>
K = :prev-folder<Enter>
H = :collapse-folder<Enter>
L = :expand-folder<Enter>
<Left> = :collapse-folder<Enter>
<Right> = :expand-folder<Enter>
# # Archive = remove from inbox
# a = :tag -inbox +archive<Enter>
# d = :move Trash<Enter>
# a = :archive flat<Enter>
# Marking/Flagging
v = :mark -v<Enter>
V = :mark -t<Enter>
<Space> = :mark -t<Enter>:next<Enter>
<Esc> = :unmark -a<Enter>
# Expunge = actually delete (run this in Trash folder)
D = :prompt 'Really delete? ' 'tag +deleted'
# Accounts
,1 = :change-tab phil<Enter>
,2 = :change-tab spam<Enter>
,3 = :change-tab proton<Enter>
# Quick folder navigation
# Folders
,i = :cf Inbox<Enter>
,u = :cf Unread<Enter>
,n = :cf Newsletters<Enter>
,f = :cf Finance<Enter>
,s = :cf Shopping<Enter>
,t = :cf Travel<Enter>
,v = :cf Travel<Enter>
,d = :cf Development<Enter>
,x = :cf Spam<Enter>
,tr = :cf Trash<Enter>
,a = :cf All Mail<Enter>
# o1 = :compose -H "From: Liph \<liiph@proton.me\>"<enter>
# o2 = :compose -H "From: Liph \<liiph@protonmail.com\>"<enter>
# o3 = :compose -H "From: Liph \<liiph@pm.me\>"<enter>
# o4 = :compose -H "From: Philipp Waibel \<ph.waibel@proton.me\>"<enter>
# o5 = :compose -H "From: P.Waibel \<ph.waibel@pm.me\>"<enter>
# Delete and sync
#d = :modify-labels +trash -inbox<Enter>:archive flat<Enter>
d = :modify-labels +deleted -inbox<Enter>
#d = :modify-labels +deleted -inbox<Enter>
#d = :move Trash<Enter>:exec mbsync -a && notmuch new<Enter>
# Permanently delete (Shift+D)
#D = :prompt 'Really delete?' :move Trash<Enter>
#B = :compose -H "From: waibel ph.waibel@proton.me"<Enter>
# for url handling
j = :next<Enter>
<Down> = :next<Enter>
<C-d> = :next 50%<Enter>
<C-f> = :next 100%<Enter>
<PgDn> = :next 100%<Enter>
k = :prev<Enter>
<Up> = :prev<Enter>
<C-u> = :prev 50%<Enter>
<C-b> = :prev 100%<Enter>
<PgUp> = :prev 100%<Enter>
g = :select 0<Enter>
G = :select -1<Enter>
J = :next-folder<Enter>
<C-Down> = :next-folder<Enter>
K = :prev-folder<Enter>
<C-Up> = :prev-folder<Enter>
H = :collapse-folder<Enter>
<C-Left> = :collapse-folder<Enter>
L = :expand-folder<Enter>
<C-Right> = :expand-folder<Enter>
v = :mark -t<Enter>
<Space> = :mark -t<Enter>:next<Enter>
V = :mark -v<Enter>
T = :toggle-threads<Enter>
zc = :fold<Enter>
zo = :unfold<Enter>
za = :fold -t<Enter>
zM = :fold -a<Enter>
zR = :unfold -a<Enter>
<tab> = :fold -t<Enter>
zz = :align center<Enter>
zt = :align top<Enter>
zb = :align bottom<Enter>
,aa = :cf All Mail<Enter>
# Actions
<Enter> = :view<Enter>
#d = :choose -o y 'Really delete this message' delete-message<Enter>
#D = :delete<Enter>
s = :split<Enter>
S = :vsplit<Enter>
d = :modify-labels +deleted -inbox<Enter>
a = :archive flat<Enter>
A = :unmark -a<Enter>:mark -T<Enter>:archive flat<Enter>
C = :compose<Enter>
m = :compose<Enter>
b = :bounce<space>
rr = :reply -a<Enter>
rq = :reply -aq<Enter>
Rr = :reply<Enter>
Rq = :reply -q<Enter>
c = :cf<space>
$ = :term<space>
! = :term<space>
| = :pipe<space>
/ = :search<space>
\ = :filter<space>
n = :next-result<Enter>
N = :prev-result<Enter>
<Esc> = :clear<Enter>
s = :split<Enter>
S = :vsplit<Enter>
pl = :patch list<Enter>
pa = :patch apply <Tab>
pd = :patch drop <Tab>
pb = :patch rebase<Enter>
pt = :patch term<Enter>
ps = :patch switch <Tab>
# Threads
T = :toggle-threads<Enter>
zc = :fold<Enter>
zo = :unfold<Enter>
za = :fold -t<Enter>
[messages:folder=Drafts]
<Enter> = :recall<Enter>
[view]
,l = :pipe urlscan<Enter>
/ = :toggle-key-passthrough<Enter>/
# Navigation & UI
q = :close<Enter>
O = :open<Enter>
o = :open<Enter>
S = :save<space>
x = :close<Enter>
| = :pipe<space>
D = :delete<Enter>
A = :archive flat<Enter>
<C-l> = :open-link <space>
f = :forward<Enter>
rr = :reply -a<Enter>
rq = :reply -aq<Enter>
Rr = :reply<Enter>
Rq = :reply -q<Enter>
H = :toggle-headers<Enter>
<C-k> = :prev-part<Enter>
<C-Up> = :prev-part<Enter>
<C-j> = :next-part<Enter>
<C-Down> = :next-part<Enter>
,l = :pipe urlscan<Enter>
# Attachment Handling
n = :next-part<Enter>
p = :prev-part<Enter>
J = :next<Enter>
<C-Right> = :next<Enter>
K = :prev<Enter>
<C-Left> = :prev<Enter>
o = :open<Enter>
O = :open<Enter>
s = :save ~/Downloads/<Enter>
S = :save<space>
,sa = :save -a ~/Downloads/<Enter>
[view::passthrough]
$noinherit = true
@@ -179,65 +117,45 @@ $ex = <C-x>
<Esc> = :toggle-key-passthrough<Enter>
[compose]
# Keybindings used when the embedded terminal is not selected in the compose
# view
# General compose view
$noinherit = true
$ex = <C-x>
$complete = <C-o>
<C-k> = :prev-field<Enter>
<C-Up> = :prev-field<Enter>
<C-j> = :next-field<Enter>
<C-Down> = :next-field<Enter>
<A-p> = :switch-account -p<Enter>
<C-Left> = :switch-account -p<Enter>
<A-n> = :switch-account -n<Enter>
<C-Right> = :switch-account -n<Enter>
<C-k> = :attach<space>
<C-a> = :attach<space>
<F4> = :attach<space>
<A-p> = :prev-tab<Enter>
<A-n> = :next-tab<Enter>
<tab> = :next-field<Enter>
<backtab> = :prev-field<Enter>
<C-p> = :prev-tab<Enter>
<C-PgUp> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
<C-PgDn> = :next-tab<Enter>
[compose::editor]
# Keybindings used when the embedded terminal is selected in the compose view
# While focused in the text editor (nvim)
$noinherit = true
$ex = <C-x>
<C-k> = :prev-field<Enter>
<C-Up> = :prev-field<Enter>
<C-j> = :next-field<Enter>
<C-Down> = :next-field<Enter>
<C-p> = :prev-tab<Enter>
<C-PgUp> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
<C-PgDn> = :next-tab<Enter>
# Insert signatures with Ctrl+s + number
<C-s>1 = :exec cat /home/liph/.config/aerc/sigs/formal.txt<Enter>
<C-s>2 = :read ~/.config/aerc/sigs/formal_eng.txt<Enter>
<C-s>3 = :read ~/.config/aerc/sigs/liph.txt<Enter>
<C-s>4 = :read ~/.config/aerc/sigs/phil.txt<Enter>
<C-k> = :attach<space>
<C-a> = :attach<space>
<F4> = :attach<space>
[compose::review]
# Keybindings used when reviewing a message to be sent
# Inline comments are used as descriptions on the review screen
y = :send<Enter> # Send
n = :abort<Enter> # Abort (discard message, no confirmation)
s = :sign<Enter> # Toggle signing
x = :encrypt<Enter> # Toggle encryption to all recipients
v = :preview<Enter> # Preview message
p = :postpone<Enter> # Postpone
q = :choose -o d discard abort -o p postpone postpone<Enter> # Abort or postpone
e = :edit<Enter> # Edit (body and headers)
a = :attach<space> # Add attachment
d = :detach<space> # Remove attachment
# After exiting the editor
y = :multipart text/html<Enter>:send<Enter>
n = :abort<Enter>
v = :preview<Enter>
p = :postpone<Enter>
q = :choose -o d discard abort -o p postpone postpone<Enter>
e = :edit<Enter>
a = :attach<space>
<C-k> = :attach<space>
<C-a> = :attach<space>
d = :detach<space>
[terminal]
$noinherit = true
# Clear keys to allow passthrough to Khal/Taskwarrior
$ex = <C-x>
<C-p> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
<C-PgUp> = :prev-tab<Enter>
<C-PgDn> = :next-tab<Enter>
<C-p> =
<C-n> =
<tab> =
<backtab> =
<A-p> = :prev-tab<Enter>
<A-n> = :next-tab<Enter>
+146
View File
@@ -0,0 +1,146 @@
# Binds are of the form <key sequence> = <command to run>
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
# GLOBAL Navigation & Contacts
( = :term abook<Enter>
,ab = :term abook<Enter>
,ca = :term khal interactive<Enter>
<C-p> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
<C-t> = :terminal<Enter>
? = :help keys<Enter>
<C-c> = :prompt 'Quit?' quit<Enter>
<C-q> = :prompt 'Quit?' quit<Enter>
<C-z> = :suspend<Enter>
[messages]
# Sync
u = :term /home/liph/.local/bin/mailsync<Enter>
# Navigation
j = :next<Enter>
k = :prev<Enter>
<Down> = :next<Enter>
<Up> = :prev<Enter>
g = :select 0<Enter>
G = :select -1<Enter>
J = :next-folder<Enter>
K = :prev-folder<Enter>
H = :collapse-folder<Enter>
L = :expand-folder<Enter>
<Left> = :collapse-folder<Enter>
<Right> = :expand-folder<Enter>
# Accounts
,1 = :change-tab phil<Enter>
,2 = :change-tab spam<Enter>
,3 = :change-tab proton<Enter>
# Folders
,i = :cf Inbox<Enter>
,u = :cf Unread<Enter>
,n = :cf Newsletters<Enter>
,f = :cf Finance<Enter>
,s = :cf Shopping<Enter>
,t = :cf Travel<Enter>
,d = :cf Development<Enter>
,x = :cf Spam<Enter>
,tr = :cf Trash<Enter>
,am = :cf All Mail<Enter>
# Actions
<Enter> = :view<Enter>
s = :split<Enter>
S = :vsplit<Enter>
d = :modify-labels +deleted -inbox<Enter>
a = :archive flat<Enter>
C = :compose<Enter>
m = :compose<Enter>
rr = :reply -a<Enter>
rq = :reply -aq<Enter>
Rr = :reply<Enter>
Rq = :reply -q<Enter>
/ = :search<space>
\ = :filter<space>
n = :next-result<Enter>
N = :prev-result<Enter>
# Threads
T = :toggle-threads<Enter>
zc = :fold<Enter>
zo = :unfold<Enter>
za = :fold -t<Enter>
[messages:folder=Drafts]
<Enter> = :recall<Enter>
[view]
# Navigation & UI
q = :close<Enter>
x = :close<Enter>
| = :pipe<space>
D = :delete<Enter>
A = :archive flat<Enter>
f = :forward<Enter>
rr = :reply -a<Enter>
rq = :reply -aq<Enter>
Rr = :reply<Enter>
Rq = :reply -q<Enter>
H = :toggle-headers<Enter>
,l = :pipe urlscan<Enter>
# Attachment Handling
n = :next-part<Enter>
p = :prev-part<Enter>
J = :next<Enter>
K = :prev<Enter>
o = :open<Enter>
O = :open<Enter>
s = :pipe -m /home/liph/.local/bin/save-attachment<Enter>
S = :save<space>
,sa = :save -a<Enter>
[view::passthrough]
$noinherit = true
$ex = <C-x>
<Esc> = :toggle-key-passthrough<Enter>
[compose]
# General compose view
$noinherit = true
$ex = <C-x>
$complete = <C-o>
<C-k> = :attach<space>
<C-a> = :attach<space>
<F4> = :attach<space>
<C-p> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
<tab> = :next-field<Enter>
<backtab> = :prev-field<Enter>
[compose::editor]
# While focused in the text editor (nvim)
$noinherit = true
$ex = <C-x>
<C-k> = :attach<space>
<C-a> = :attach<space>
<F4> = :attach<space>
[compose::review]
# After exiting the editor
y = :multipart text/html<Enter>:send<Enter>
n = :abort<Enter>
v = :preview<Enter>
p = :postpone<Enter>
q = :choose -o d discard abort -o p postpone postpone<Enter>
e = :edit<Enter>
a = :attach<space>
<C-k> = :attach<space>
<C-a> = :attach<space>
d = :detach<space>
[terminal]
$noinherit = true
$ex = <C-x>
<C-p> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
+22 -27
View File
@@ -1,42 +1,37 @@
# Priority Management
Priority 1=tag:priority1 and tag:inbox
Priority 2=tag:priority2 and tag:inbox
All Priority=tag:priority1 or tag:priority2
Priority/1=tag:priority1 and tag:inbox and not tag:deleted and not tag:spam
Priority/2=tag:priority2 and tag:inbox and not tag:deleted and not tag:spam
Priority/All=(tag:priority1 or tag:priority2) and not tag:deleted and not tag:spam
# Main Views
Inbox=tag:inbox
Unread=tag:unread
Starred=tag:flagged or tag:starred
Inbox=tag:inbox and not tag:deleted and not tag:spam and not (from:@makesomebreathingspace.com or from:@healplaylove.ch or from:@circle.so)
Unread=tag:unread and not tag:deleted and not tag:spam
Starred=(tag:flagged or tag:starred) and not tag:deleted and not tag:spam
Archive=not tag:inbox and not tag:spam and not tag:deleted
Sent=folder:Sent or tag:sent
Drafts=folder:Drafts or tag:draft
Spam=tag:spam or folder:Spam
Trash=tag:deleted or folder:Trash
Mail/Sent=(folder:Sent or tag:sent) and not tag:deleted
Mail/Drafts=folder:Drafts or tag:draft
Mail/Spam=tag:spam and not tag:deleted
Mail/Trash=tag:deleted
# Categories
# Work=tag:work and tag:inbox
# Personal=tag:personal and tag:inbox
Finance=tag:finance and tag:inbox
# Social=tag:social and tag:inbox
# Mailing Lists=tag:mailinglist and tag:inbox
Newsletters=tag:newsletter and tag:inbox
# Automated=tag:automated
Finance=tag:finance and tag:inbox and not tag:deleted and not tag:spam
Newsletters=tag:newsletter and tag:inbox and not tag:deleted and not tag:spam
# Time-based
Today=date:today and tag:inbox
This Week=date:week and tag:inbox
This Month=date:month and tag:inbox
Last 7 Days=date:7days..today
Time/Today=date:today and tag:inbox and not tag:deleted and not tag:spam
Time/This Week=date:6days..today and tag:inbox and not tag:deleted and not tag:spam
Time/This Month=date:month and tag:inbox and not tag:deleted and not tag:spam
Time/Last 7 Days=date:7days..today and not tag:deleted and not tag:spam
# Special Queries
Needs Reply=tag:inbox and not tag:replied
Attachments=tag:attachment and tag:inbox
Important=tag:flagged or tag:priority1
Action Required=tag:todo or tag:followup
Needs Reply=tag:inbox and not tag:replied and not tag:deleted and not tag:spam
Attachments=tag:attachment and tag:inbox and not tag:deleted and not tag:spam
Important=(tag:flagged or tag:priority1) and not tag:deleted and not tag:spam
Action Required=(tag:todo or tag:followup) and not tag:deleted and not tag:spam
# Unprocessed
New Mail=tag:new
Untagged=tag:inbox and not tag:priority1 and not tag:priority2 and not tag:work and not tag:personal and not tag:finance
New Mail=tag:new and not tag:deleted and not tag:spam
Untagged=tag:inbox and not tag:priority1 and not tag:priority2 and not tag:work and not tag:personal and not tag:finance and not tag:deleted and not tag:spam
# All Mail
Everything=*
+44
View File
@@ -0,0 +1,44 @@
# Standard Main Views
Inbox=tag:inbox and not tag:deleted and not tag:spam and not (from:@makesomebreathingspace.com or from:@healplaylove.ch or from:@circle.so)
Unread=tag:unread and not tag:deleted and not tag:spam
Starred=(tag:flagged or tag:starred) and not tag:deleted and not tag:spam
Archive=not tag:inbox and not tag:spam and not tag:deleted
Mail/Sent=(folder:Sent or tag:sent) and not tag:deleted
Mail/Drafts=folder:Drafts or tag:draft
Mail/Spam=tag:spam and not tag:deleted
Mail/Trash=tag:deleted
# Community Folder Stack
Community=(from:@makesomebreathingspace.com or from:@healplaylove.ch or from:@circle.so) and date:today and not tag:deleted and not tag:spam
# Community - Breathing Space
Community/Breathing Space=from:@makesomebreathingspace.com and not tag:deleted and not tag:spam
Community/Breathing Space/Today=from:@makesomebreathingspace.com and date:today and not tag:deleted and not tag:spam
Community/Breathing Space/This Week=from:@makesomebreathingspace.com and date:6days..today and not tag:deleted and not tag:spam
# Community - Heal Play Love
Community/Heal Play Love=(from:@healplaylove.ch or from:@circle.so) and not tag:deleted and not tag:spam
Community/Heal Play Love/Today=(from:@healplaylove.ch or from:@circle.so) and date:today and not tag:deleted and not tag:spam
Community/Heal Play Love/This Week=(from:@healplaylove.ch or from:@circle.so) and date:6days..today and not tag:deleted and not tag:spam
# Categories
Finance=tag:finance and tag:inbox and not tag:deleted and not tag:spam
Newsletters=tag:newsletter and tag:inbox and not tag:deleted and not tag:spam
# Priority Management
Priority/1=tag:priority1 and tag:inbox and not tag:deleted and not tag:spam
Priority/2=tag:priority2 and tag:inbox and not tag:deleted and not tag:spam
Priority/All=(tag:priority1 or tag:priority2) and not tag:deleted and not tag:spam
# Time-based
Time/Today=date:today and tag:inbox and not tag:deleted and not tag:spam
Time/This Week=date:week and tag:inbox and not tag:deleted and not tag:spam
Time/Last 7 Days=date:7days..today and not tag:deleted and not tag:spam
# Status/Action
Action/Needs Reply=tag:inbox and not tag:replied and not tag:deleted and not tag:spam
Action/Important=(tag:flagged or tag:priority1) and not tag:deleted and not tag:spam
Action/Todo=(tag:todo or tag:followup) and not tag:deleted and not tag:spam
# All Mail
Everything=*
+35
View File
@@ -0,0 +1,35 @@
# Standard Main Views
Inbox=tag:inbox and not tag:deleted and not tag:spam
Unread=tag:unread and not tag:deleted and not tag:spam
Starred=(tag:flagged or tag:starred) and not tag:deleted and not tag:spam
Archive=not tag:inbox and not tag:spam and not tag:deleted
Mail/Sent=(folder:Sent or tag:sent) and not tag:deleted
Mail/Drafts=folder:Drafts or tag:draft
Mail/Spam=tag:spam and not tag:deleted
Mail/Trash=tag:deleted
# Community Folder Stack
Community/Breathing Space=from:@makesomebreathingspace.com and not tag:deleted and not tag:spam
Community/Heal Play Love=(from:@healplaylove.ch or from:@circle.so) and not tag:deleted and not tag:spam
# Categories
Finance=tag:finance and tag:inbox and not tag:deleted and not tag:spam
Newsletters=tag:newsletter and tag:inbox and not tag:deleted and not tag:spam
# Priority Management
Priority/1=tag:priority1 and tag:inbox and not tag:deleted and not tag:spam
Priority/2=tag:priority2 and tag:inbox and not tag:deleted and not tag:spam
Priority/All=(tag:priority1 or tag:priority2) and not tag:deleted and not tag:spam
# Time-based
Time/Today=date:today and tag:inbox and not tag:deleted and not tag:spam
Time/This Week=date:week and tag:inbox and not tag:deleted and not tag:spam
Time/Last 7 Days=date:7days..today and not tag:deleted and not tag:spam
# Status/Action
Action/Needs Reply=tag:inbox and not tag:replied and not tag:deleted and not tag:spam
Action/Important=(tag:flagged or tag:priority1) and not tag:deleted and not tag:spam
Action/Todo=(tag:todo or tag:followup) and not tag:deleted and not tag:spam
# All Mail
Everything=*
@@ -1,51 +0,0 @@
*.default=true
*.normal=true
default.fg=#cdd6f4
error.fg=#f38ba8
warning.fg=#fab387
success.fg=#a6e3a1
tab.fg=#6c7086
tab.bg=#181825
tab.selected.fg=#cdd6f4
tab.selected.bg=#1e1e2e
tab.selected.bold=true
border.fg=#11111b
border.bold=true
msglist_unread.bold=true
msglist_flagged.fg=#f9e2af
msglist_flagged.bold=true
msglist_result.fg=#89b4fa
msglist_result.bold=true
msglist_*.selected.bold=true
msglist_*.selected.bg=#313244
dirlist_*.selected.bold=true
dirlist_*.selected.bg=#313244
statusline_default.fg=#9399b2
statusline_default.bg=#313244
statusline_error.bold=true
statusline_success.bold=true
selector_focused.bg=#313244
completion_default.selected.bg=#313244
[viewer]
url.fg=#89b4fa
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#89b4fa
diff_chunk_func.fg=#89b4fa
diff_chunk_func.bold=true
diff_add.fg=#a6e3a1
diff_del.fg=#f38ba8
quote_*.fg=#6c7086
quote_1.fg=#9399b2
+59
View File
@@ -0,0 +1,59 @@
*.default=true
*.normal=true
*.selected.bold=true
*.selected.bg=#ebdbb2
*.selected.fg=#282828
default.fg=#89b482
error.fg=#cc241d
warning.fg=#d65d0e
success.fg=#D8A657
tab.fg=#7c6f64
tab.bg=#282823
tab.selected.fg=#d5c4a1
tab.selected.bg=#282828
tab.selected.bold=true
border.fg=#d5c4a1
border.bold=true
msglist_unread.bold=true
msglist_unread.fg=#d8a657
msglist_unread.selected.bg=#ebdbb2
msglist_unread.selected.fg=#282828
msglist_flagged.fg=#f9e2af
msglist_flagged.bold=true
msglist_result.fg=#458588
msglist_result.bold=true
msglist_marked.fg=#d3869b
msglist_marked.bold=true
msglist_*.selected.bold=true
msglist_*.selected.bg=#ebdbb2
dirlist_*.selected.bold=true
dirlist_*.selected.bg=#ebdbb2
statusline_default.fg=#ebdbb2
statusline_default.bg=#282828
statusline_error.bold=true
statusline_success.bold=true
[user]
folder_sub.fg=#fabd2f
folder_sub.bold=true
[viewer]
url.fg=#458588
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#458588
diff_chunk_func.fg=#458588
diff_chunk_func.bold=true
diff_add.fg=#D8A657
diff_del.fg=#cc241d
quote_*.fg=#d5c4a1
quote_1.fg=#b16286
+85
View File
@@ -0,0 +1,85 @@
# vim: filetype=dosini
# Rosé Pine Theme for Aerc
# All natural pine, faux fur and a bit of soho vibes for the classy minimalist
# Save as ~/.config/aerc/stylesets/rose-pine
# Base styling
default.fg=#9ccfd8
default.bg=#191724
success.fg=#31748f
error.fg=#eb6f92
warning.fg=#f6c177
title.bg=#26233a
title.fg=#c4a7e7
header.fg=#c4a7e7
spinner.fg=#6e6a86
part_filename.fg=#f6c177
part_mimetype.fg=#c4a7e7
# Global selection
*.selected.fg=#191724
*.selected.bg=#ebbcba
*.selected.bold=true
# Message list
msglist_default.fg=#9ccfd8
msglist_default.bg=#1f1d2e
msglist_unread.fg=#524f67
msglist_unread.bold=true
msglist_read.fg=#6e6a86
msglist_flagged.fg=#f6c177
msglist_flagged.bold=true
msglist_deleted.bg=#eb6f92
msglist_deleted.fg=#191724
msglist_deleted.bold=true
msglist_marked.fg=#191724
msglist_marked.bg=#31748f
msglist_marked.bold=true
# Status bar
statusline_*.bold=true
statusline_*.fg=#191724
statusline_default.fg=#9ccfd8
statusline_default.bg=#191724
statusline_error.bg=#eb6f92
statusline_success.bg=#31748f
# Completion
completion_default.fg=#9ccfd8
completion_default.bg=#1f1d2e
completion_gutter.bg=#26233a
completion_description.bg=#26233a
# Scrollbars
*_pill.fg=#191724
*_pill.bg=#6e6a86
# Border
border.fg=#191724
border.bg=#191724
# Tab
tab.bg=#191724
tab.selected.fg=#ebbcba
tab.selected.bg=#191724
tab.selected.bold=true
[viewer]
url.fg=#c4a7e7
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#c4a7e7
diff_chunk_func.fg=#c4a7e7
diff_chunk_func.bold=true
diff_add.fg=#524f67
diff_del.fg=#eb6f92
quote_*.fg=#908caa
quote_1.fg=#21202e
+85
View File
@@ -0,0 +1,85 @@
# vim: filetype=dosini
# Rosé Pine Theme for Aerc
# All natural pine, faux fur and a bit of soho vibes for the classy minimalist
# Save as ~/.config/aerc/stylesets/rose-pine
# Base styling
default.fg=#9ccfd8
default.bg=#191724
success.fg=#31748f
error.fg=#eb6f92
warning.fg=#f6c177
title.bg=#26233a
title.fg=#c4a7e7
header.fg=#c4a7e7
spinner.fg=#6e6a86
part_filename.fg=#f6c177
part_mimetype.fg=#c4a7e7
# Global selection
*.selected.fg=#191724
*.selected.bg=#ebbcba
*.selected.bold=true
# Message list
msglist_default.fg=#9ccfd8
msglist_default.bg=#1f1d2e
msglist_unread.fg=#524f67
msglist_unread.bold=true
msglist_read.fg=#6e6a86
msglist_flagged.fg=#f6c177
msglist_flagged.bold=true
msglist_deleted.bg=#eb6f92
msglist_deleted.fg=#191724
msglist_deleted.bold=true
msglist_marked.fg=#191724
msglist_marked.bg=#31748f
msglist_marked.bold=true
# Status bar
statusline_*.bold=true
statusline_*.fg=#191724
statusline_default.fg=#9ccfd8
statusline_default.bg=#191724
statusline_error.bg=#eb6f92
statusline_success.bg=#31748f
# Completion
completion_default.fg=#9ccfd8
completion_default.bg=#1f1d2e
completion_gutter.bg=#26233a
completion_description.bg=#26233a
# Scrollbars
*_pill.fg=#191724
*_pill.bg=#6e6a86
# Border
border.fg=#191724
border.bg=#191724
# Tab
tab.bg=#191724
tab.selected.fg=#ebbcba
tab.selected.bg=#191724
tab.selected.bold=true
[viewer]
url.fg=#c4a7e7
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#c4a7e7
diff_chunk_func.fg=#c4a7e7
diff_chunk_func.bold=true
diff_add.fg=#524f67
diff_del.fg=#eb6f92
quote_*.fg=#908caa
quote_1.fg=#21202e
+42
View File
@@ -0,0 +1,42 @@
# Ansible
*.retry
.vault_pass*
/tmp/
.ansible/
# Generated inventory (regenerate with setup.sh)
inventory/hosts.yml
inventory/group_vars/all/vars.yml
# Keep vault.yml encrypted (DO NOT ignore it - it should be committed encrypted)
# inventory/group_vars/all/vault.yml
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
# Virtual environments
venv/
env/
ENV/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Temporary files
*.tmp
*.bak
+259
View File
@@ -0,0 +1,259 @@
# Agent Guidelines for Ansible Infrastructure Repository
This repository contains Docker Compose infrastructure configurations for deploying self-hosted productivity services. This document provides guidelines for AI coding agents working in this repository.
## Project Overview
**Type:** Docker Compose Infrastructure Project
**Primary Technology:** Docker, Docker Compose
**Purpose:** Deployment configurations for self-hosted productivity stack including Nextcloud, OnlyOffice, Excalidraw, and Obsidian
## Repository Structure
```
/home/liph/programming/ansible/
└── nextcloud/
├── docker-compose.yml # Main orchestration file (5 services)
└── .env # Environment variables
```
## Commands
### Docker Compose Operations
```bash
# Navigate to service directory
cd nextcloud/
# Start all services
docker compose up -d
# Start specific service
docker compose up -d <service-name>
# Stop all services
docker compose down
# Stop and remove volumes (DESTRUCTIVE)
docker compose down -v
# View logs for all services
docker compose logs -f
# View logs for specific service
docker compose logs -f <service-name>
# Restart a service
docker compose restart <service-name>
# Rebuild and restart service
docker compose up -d --build <service-name>
# Validate docker-compose.yml syntax
docker compose config
# List running services
docker compose ps
```
### Service Names
- `excalidraw` - Drawing/whiteboard tool (port 3009)
- `next-db` - PostgreSQL 18 database
- `next` - Nextcloud main application (port 8808)
- `onlyoffice` - Document server (port 8000)
- `obsidian` - Note-taking app (ports 3004-3005)
### Testing
No automated tests exist. Manual testing workflow:
1. Validate syntax: `docker compose config`
2. Start services: `docker compose up -d`
3. Check health: `docker compose ps`
4. Review logs: `docker compose logs`
5. Test endpoints: `curl localhost:8808` (Nextcloud), etc.
## Code Style Guidelines
### YAML Formatting (docker-compose.yml)
**Indentation:**
- Use 2 spaces for indentation (NO tabs)
- Maintain consistent indentation levels
**Structure:**
```yaml
services:
service-name:
image: docker.io/image:tag
container_name: container-name
depends_on:
- dependency
ports:
- "host:container"
environment:
- KEY=value
- KEY=${ENV_VAR}
volumes:
- volume_name:/container/path
- ./host/path:/container/path
restart: unless-stopped
networks:
- network_name
```
**Naming Conventions:**
- Container names: Use kebab-case (e.g., `next-db`, `next-redis`)
- Service names: Use snake_case or kebab-case consistently
- Volume names: Use snake_case (e.g., `nextcloud_data`, `pg_data`)
- Network names: Use snake_case with project prefix (e.g., `nextcloud_network`)
**Environment Variables:**
- Reference .env variables using `${VAR_NAME}` syntax
- Never hardcode sensitive data (passwords, secrets) in docker-compose.yml
- Use explicit environment variable declarations with `-` prefix
**Image Specifications:**
- Always use fully qualified image names: `docker.io/image:tag`
- Pin specific versions for production (avoid `:latest` in production)
- Current exception: development environment uses `:latest` tags
**Volumes:**
- Named volumes for persistent data (defined in volumes section)
- Bind mounts for configuration files using `./relative/path`
- Always specify volume mount destination path
- CRITICAL: Never leave empty volume definitions (e.g., `- :/path`)
**Comments:**
- Use `#` for comments
- Add comments above service definitions to describe purpose
- Comment out optional services with `# ` prefix on each line
### Environment Files (.env)
**Format:**
```bash
# Database Configuration
DB_PASSWORD=secure_password_here
DB_USERNAME=nextcloud
DB_DATABASE_NAME=nextcloud
DB_HOST=next-db
# User/Group IDs
PUID=33
PGID=1000
```
**Rules:**
- One variable per line: `KEY=value`
- No spaces around `=`
- No quotes needed for values
- Group related variables with comment headers
- Never commit sensitive values (use placeholders or .env.example)
- All variables must have values (no empty assignments)
### Security Guidelines
1. **Secrets Management:**
- Never hardcode passwords in docker-compose.yml
- Use .env file for credentials (ensure .env is in .gitignore)
- Consider Docker secrets for sensitive production data
- Rotate default passwords immediately
2. **Current Security Issues to Fix:**
- Line 53 in docker-compose.yml: Remove hardcoded admin password
- .env file: All database credentials are empty
- Consider enabling JWT for OnlyOffice (currently disabled)
3. **Network Security:**
- Use isolated Docker networks for service communication
- Only expose necessary ports to host
- Consider reverse proxy (nginx/traefik) for HTTPS termination
### Error Handling
**Validation Steps Before Committing:**
1. Run `docker compose config` to validate YAML syntax
2. Ensure all volume mount paths are complete
3. Verify all environment variables are defined in .env
4. Check for hardcoded secrets
5. Confirm port conflicts with `docker compose ps`
**Common Issues:**
- Empty volume definitions: Always specify source and destination
- Missing environment variables: Define all referenced vars in .env
- Port conflicts: Use `docker ps` to check existing port bindings
- Network errors: Ensure services on same network can communicate
### Configuration Management
**Volume Mapping Patterns:**
```yaml
# Named volume (recommended for data)
volumes:
- nextcloud_data:/var/www/html
# Bind mount with SELinux label (for config)
volumes:
- ./config:/var/www/html/config:Z
# Bind mount with shared label
volumes:
- ./vault:/vault:z
```
**Restart Policies:**
- Use `restart: unless-stopped` for production services
- Avoid `restart: always` (harder to stop deliberately)
- Use `restart: on-failure` for development/debugging
### Database Configuration
**PostgreSQL Environment Variables:**
- `POSTGRES_DB` - Database name
- `POSTGRES_USER` - Database user
- `POSTGRES_PASSWORD` - Database password
**Nextcloud Database Connection:**
- `POSTGRES_HOST` - Hostname (service name: `next-db`)
- Must match database service configuration
### Best Practices
1. **Service Dependencies:**
- Use `depends_on` to define startup order
- Note: `depends_on` doesn't wait for "ready" state
2. **Resource Limits:**
- Consider adding memory/CPU limits for production
- Current config uses `mem_swappiness: -1` for Nextcloud
3. **Timezone Configuration:**
- Set `TZ` environment variable for correct timestamps
- Current: `TZ=Europe/Zurich` (Excalidraw), `TZ=Etc/UTC` (Obsidian)
4. **Data Persistence:**
- Always use named volumes for important data
- Define volumes in top-level `volumes:` section
- Backup volume data regularly
5. **Making Changes:**
- Test changes in development before production
- Use `docker compose config` to validate
- Review diff carefully before applying
- Document breaking changes
## Development Workflow
1. Make changes to docker-compose.yml or .env
2. Validate: `docker compose config`
3. Apply changes: `docker compose up -d`
4. Verify: `docker compose ps && docker compose logs`
5. Test affected services manually
6. Document changes in commit message
## Critical Fixes Needed
- [ ] Complete empty volume mount paths (lines 21, 55-57, 74, 89-90)
- [ ] Remove hardcoded admin password (line 53)
- [ ] Populate database credentials in .env file
- [ ] Add .gitignore to exclude .env from version control
- [ ] Consider adding docker-compose.override.yml for local development
+426
View File
@@ -0,0 +1,426 @@
# Network Architecture
Visual overview of how all services connect and communicate.
## 🌐 Network Flow Diagram
```
INTERNET
│ DNS A Records
│ (cloud.yourdomain.com → SERVER_IP)
┌───────────────┐
│ Firewall (UFW)│
│ Ports: 80 │
│ 443 │
│ 41641 │
└───────┬───────┘
┌────────────────┼────────────────┐
│ │ │
Port 80/443 Port 41641 Port 22
│ │ │
↓ ↓ ↓
┌──────────────┐ ┌─────────────┐ ┌──────────┐
│ CADDY │ │ TAILSCALE │ │ SSH │
│ Reverse Proxy│ │ VPN │ │ Access │
└──────┬───────┘ └─────┬───────┘ └──────────┘
│ │
│ │
┌──────────┴────────┐ │
│ │ │
↓ ↓ ↓
┌─────────┐ ┌─────────────────┐
│ PUBLIC │ │ TAILSCALE-ONLY │
│SERVICES │ │ SERVICES │
└─────────┘ └─────────────────┘
```
---
## 🔐 Access Matrix
| Service | Public URL | Tailscale URL | Direct Access |
|---------|-----------|---------------|---------------|
| **Nextcloud** | ✅ https://cloud.domain.com | ✅ Via Tailscale IP | ❌ No |
| **OnlyOffice** | ✅ https://office.domain.com | ✅ Via Tailscale IP | ❌ No |
| **Excalidraw** | ✅ https://draw.domain.com | ✅ Via Tailscale IP | ❌ No |
| **Obsidian** | ✅ https://notes.domain.com | ✅ Via Tailscale IP | ❌ No |
| **Homarr** | ❌ 403 Forbidden | ✅ https://home.domain.com | ❌ No |
| **Dockhand** | ❌ 403 Forbidden | ✅ https://manage.domain.com | ❌ No |
| **Uptime Kuma** | ❌ 403 Forbidden | ✅ https://uptime.domain.com | ❌ No |
| **PostgreSQL** | ❌ No | ❌ No | ✅ Docker network only |
| **Redis** | ❌ No | ❌ No | ✅ Docker network only |
| **Caddy** | ❌ No (serves others) | ❌ No | ✅ Docker network only |
| **Watchtower** | ❌ No | ❌ No | ✅ Docker network only |
---
## 🐳 Docker Network Topology
```
┌────────────────────────────────────────────────────────────────┐
│ nextcloud_network (bridge) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │PostgreSQL│ │ Redis │ │Nextcloud │ │OnlyOffice│ │
│ │ :5432 │ │ :6379 │ │ :80 │ │ :80 │ │
│ └──────────┘ └──────────┘ └────┬─────┘ └──────────┘ │
│ ↑ ↑ │ │
│ │ │ │ │
│ └──────────────┴─────────────┘ │
│ │ │
│ ┌──────────┐ ┌────┴─────┐ ┌──────────┐ ┌──────────┐ │
│ │Excalidraw│ │ Caddy │ │ Obsidian │ │ Homarr │ │
│ │ :80 │ │80→:80 │ │ :3000 │ │ :7575 │ │
│ └──────────┘ │443→:443 │ └──────────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌──────────┐ │ │ ┌──────────┐ ↓ │
│ │ Dockhand │ │ │ │ Uptime │ /var/run/ │
│ │ :3000 │ │ │ │ Kuma │ docker.sock │
│ └──────────┘ └──────────┘ │ :3001 │ (read-only) │
│ └──────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Watchtower (monitors all) │ │
│ │ Accesses: /var/run/docker.sock │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
│ │
│ Exposed Ports │ Internal
│ │ Communication
↓ ↓
Host: 80, 443 Container-to-Container
(Caddy only) via Docker DNS
```
---
## 🔄 Request Flow Examples
### Example 1: User Accesses Nextcloud
```
User Browser
│ HTTPS GET https://cloud.yourdomain.com
Internet
Firewall (Port 443)
Caddy Container (:443)
│ Validates SSL certificate
│ Checks Caddyfile rules
│ Proxies to backend
Nextcloud Container (:80)
│ Checks trusted domains
│ Queries PostgreSQL
│ Checks Redis cache
PostgreSQL + Redis
│ Returns data
Nextcloud → Caddy → User
```
### Example 2: User Accesses Homarr (via Tailscale)
```
User Browser (on Tailscale)
│ HTTPS GET https://home.yourdomain.com
Internet → DNS Resolution
Firewall (Port 443)
Caddy Container
│ Checks client IP: 100.64.x.x (Tailscale range)
│ ✅ Match! Proxy to backend
Homarr Container (:7575)
│ Reads Docker socket
│ Shows container status
Returns dashboard → Caddy → User
```
### Example 3: User Accesses Homarr (NOT on Tailscale)
```
User Browser (public internet)
│ HTTPS GET https://home.yourdomain.com
Internet → DNS Resolution
Firewall (Port 443)
Caddy Container
│ Checks client IP: NOT in 100.64.0.0/10
│ ❌ No match! Return 403 Forbidden
403 Access Denied
```
### Example 4: Nextcloud Connects to Database
```
Nextcloud Container
│ Docker DNS lookup: next-db
│ Resolves to: 172.20.0.x
PostgreSQL Container (next-db:5432)
│ Internal Docker network
│ No external exposure
Returns query results
```
---
## 📊 Data Flow Diagram
### File Upload to Nextcloud
```
User → Caddy → Nextcloud → Storage Volume
├→ PostgreSQL (metadata)
└→ Redis (cache)
```
### Document Editing with OnlyOffice
```
User → Caddy → Nextcloud → Caddy → OnlyOffice
│ │
↓ ↓
PostgreSQL Document
(metadata) Processing
│ │
↓ ↓
Nextcloud ←───────── OnlyOffice
Storage Volume
```
### Watchtower Auto-Update
```
Watchtower (cron: every 24h)
│ Check Docker Hub for new images
New image available?
├─ NO → Sleep
├─ YES → Check container labels
├─ "enable=false" → Skip
├─ "enable=true" → Pull & Update
└─ "monitor-only=true" → Notify
```
---
## 🔐 Security Boundaries
```
┌─────────────────────────────────────────────────────────────┐
│ INTERNET (Untrusted) │
└────────────────────────┬────────────────────────────────────┘
┌────────▼────────┐
│ UFW Firewall │ ← Layer 1: Host Firewall
│ 22, 80, 443 │
└────────┬────────┘
┌────────▼────────┐
│ fail2ban │ ← Layer 2: Intrusion Detection
│ SSH Protection │
└────────┬────────┘
┌────────▼────────┐
│ Caddy (HTTPS) │ ← Layer 3: TLS Termination
│ SSL Validation │
└────────┬────────┘
┌────────────────┼────────────────┐
│ │ │
┌───▼────┐ ┌────▼────┐ ┌─────▼─────┐
│ Public │ │Tailscale│ │ Docker │ ← Layer 4: Network
│Services│ │ Check │ │ Network │ Isolation
└────────┘ └─────────┘ └───────────┘
┌────▼────┐
│ Admin │ ← Layer 5: Admin Access
│ Services│ (Tailscale VPN only)
└─────────┘
```
---
## 🗄️ Data Persistence
### Docker Volumes
```
Host Filesystem
├── /var/lib/docker/volumes/
│ ├── nextcloud_pg_data/ ← PostgreSQL database
│ ├── nextcloud_redis_data/ ← Redis cache
│ ├── nextcloud_nextcloud_data/ ← Nextcloud files
│ ├── nextcloud_nextcloud_apps/ ← Nextcloud apps
│ ├── nextcloud_onlyoffice_data/ ← OnlyOffice docs
│ ├── nextcloud_caddy_data/ ← SSL certificates
│ ├── nextcloud_caddy_config/ ← Caddy config
│ ├── nextcloud_dockhand_data/ ← Dockhand settings
│ └── nextcloud_uptime_kuma_data/ ← Uptime Kuma data
└── /opt/nextcloud-stack/
├── configs/
│ ├── caddy/Caddyfile
│ └── nextcloud/
├── data/
│ ├── homarr/
│ └── obsidian/
└── backups/
├── database/
└── volumes/
```
---
## 🔄 Update Mechanism
### Watchtower Label-Based Control
```yaml
# Auto-update allowed
labels:
- "com.centurylinklabs.watchtower.enable=true"
→ Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
# Monitor only (notify, no update)
labels:
- "com.centurylinklabs.watchtower.monitor-only=true"
→ OnlyOffice
# Never auto-update (manual only)
labels:
- "com.centurylinklabs.watchtower.enable=false"
→ Nextcloud, PostgreSQL, Redis
```
---
## 📡 Monitoring Architecture
```
┌────────────────────────────────────────────────────────┐
│ Uptime Kuma │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ HTTP Monitors (every 60s) │ │
│ │ • https://cloud.yourdomain.com/status.php │ │
│ │ • https://office.yourdomain.com/health │ │
│ │ • https://draw.yourdomain.com │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ TCP Port Monitors │ │
│ │ • next-db:5432 (PostgreSQL) │ │
│ │ • next-redis:6379 (Redis) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ SSL Certificate Monitor │ │
│ │ • Expiry check (30-day warning) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Notifications │ │
│ │ • Email alerts │ │
│ │ • Slack/Discord webhooks (optional) │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
```
---
## 🎯 Service Dependencies
```
Nextcloud
├─ requires → PostgreSQL (healthy)
├─ requires → Redis (healthy)
└─ requires → Caddy (for external access)
OnlyOffice
└─ requires → Caddy (for external access)
Homarr, Dockhand
├─ requires → Docker Socket (read-only)
└─ requires → Caddy (for Tailscale access)
Watchtower
└─ requires → Docker Socket (read-write)
All Services
└─ network → nextcloud_network
```
---
## 🌟 Architecture Benefits
### High Availability Features
- **Health checks**: Containers auto-restart on failure
- **Database connection pooling**: Redis caching reduces DB load
- **SSL auto-renewal**: Caddy handles certificate lifecycle
### Security Layers
1. **Network**: UFW firewall + fail2ban
2. **Transport**: TLS/SSL via Caddy
3. **Access**: Tailscale VPN for admin interfaces
4. **Secrets**: Ansible Vault encryption
5. **Isolation**: Docker network segmentation
### Scalability Points
- **Database**: Can migrate to external PostgreSQL
- **Cache**: Redis can be scaled/clustered
- **Storage**: Volumes can be moved to network storage
- **Reverse Proxy**: Caddy can be load-balanced
---
**This architecture provides a secure, maintainable, and production-ready self-hosted cloud platform.**
+420
View File
@@ -0,0 +1,420 @@
# Backup and Restore Guide
Complete guide for backing up and restoring your Nextcloud Stack.
## Automated Backups
### What Gets Backed Up
- **Daily (3:00 AM)**:
- PostgreSQL database dump
- **Weekly (Sundays, 3:00 AM)**:
- Nextcloud data volume
- Configuration files
- **Retention**: 30 days
### Backup Locations
```
/opt/nextcloud-stack/backups/
├── database/
│ └── nextcloud_db_YYYYMMDD_HHMMSS.sql.gz
├── volumes/
│ ├── nextcloud_data_YYYYMMDD_HHMMSS.tar.gz
│ └── configs_YYYYMMDD_HHMMSS.tar.gz
└── backup.log
```
### Manual Backup
```bash
ssh user@server
/opt/nextcloud-stack/backup.sh
```
### Check Backup Status
```bash
# View backup log
tail -f /opt/nextcloud-stack/backups/backup.log
# List backups
ls -lh /opt/nextcloud-stack/backups/database/
ls -lh /opt/nextcloud-stack/backups/volumes/
# Check disk usage
du -sh /opt/nextcloud-stack/backups/
```
---
## Restore Procedures
### 1. Restore Database Only
**When to use:**
- Database corruption
- Accidental data deletion
- Need to revert to earlier state
**Steps:**
```bash
# 1. Stop Nextcloud (keep database running)
cd /opt/nextcloud-stack
docker compose stop next
# 2. List available backups
ls -lh backups/database/
# 3. Choose backup to restore
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"
# 4. Drop existing database (CAUTION!)
docker exec next-db psql -U nextcloud -c "DROP DATABASE nextcloud;"
docker exec next-db psql -U nextcloud -c "CREATE DATABASE nextcloud;"
# 5. Restore from backup
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud
# 6. Restart Nextcloud
docker compose start next
# 7. Run Nextcloud upgrade (if needed)
docker exec -u www-data next php occ upgrade
# 8. Disable maintenance mode
docker exec -u www-data next php occ maintenance:mode --off
```
### 2. Restore Data Volumes
**When to use:**
- Lost files
- Corrupted data directory
- Complete system failure
**Steps:**
```bash
# 1. Stop all services
cd /opt/nextcloud-stack
docker compose down
# 2. List available backups
ls -lh backups/volumes/
# 3. Choose backup
BACKUP_FILE="backups/volumes/nextcloud_data_20260216_030000.tar.gz"
# 4. Restore volume
# Note: This requires accessing the Docker volume directory
sudo tar -xzf $BACKUP_FILE -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 5. Restore configs (optional)
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/
# 6. Fix permissions
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 7. Start services
docker compose up -d
# 8. Run Nextcloud file scan
docker exec -u www-data next php occ files:scan --all
```
### 3. Complete System Restore
**When to use:**
- Disaster recovery
- Migration to new server
- Complete system failure
**Steps:**
```bash
# 1. On new server, install base system
# Run Ansible playbooks 01-04 (up to Tailscale)
ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
ansible-playbook playbooks/02-system-setup.yml --ask-vault-pass
ansible-playbook playbooks/03-docker-setup.yml --ask-vault-pass
ansible-playbook playbooks/04-tailscale-setup.yml --ask-vault-pass
# 2. Create deployment directory structure
ssh user@new-server
sudo mkdir -p /opt/nextcloud-stack/backups/{database,volumes}
# 3. Copy backups from old server to new server
# On your local machine:
scp -r old-server:/opt/nextcloud-stack/backups/* user@new-server:/opt/nextcloud-stack/backups/
# 4. Deploy stack (creates volumes and containers)
ansible-playbook playbooks/05-deploy-stack.yml --ask-vault-pass
# 5. Stop services on new server
ssh user@new-server
cd /opt/nextcloud-stack
docker compose down
# 6. Restore database
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"
docker compose up -d next-db next-redis
sleep 10
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud
# 7. Restore data volumes
VOLUME_BACKUP="backups/volumes/nextcloud_data_20260216_030000.tar.gz"
sudo tar -xzf $VOLUME_BACKUP -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 8. Restore configs
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/
# 9. Fix permissions
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 10. Start all services
docker compose up -d
# 11. Run Nextcloud maintenance
docker exec -u www-data next php occ maintenance:mode --off
docker exec -u www-data next php occ files:scan --all
docker exec -u www-data next php occ db:add-missing-indices
# 12. Continue with remaining playbooks
exit
ansible-playbook playbooks/06-configure-caddy.yml --ask-vault-pass
ansible-playbook playbooks/07-setup-backups.yml --ask-vault-pass
ansible-playbook playbooks/08-post-deployment.yml --ask-vault-pass
```
---
## Off-Site Backups with rclone
### Initial Setup
```bash
ssh user@server
# Configure rclone
rclone config
# Example: Setup Backblaze B2
# Follow prompts:
# - Choose: New remote
# - Name: b2backup
# - Storage: Backblaze B2
# - Enter account ID and application key
```
### Sync Backups to Remote
```bash
# Test sync (dry run)
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups --dry-run
# Actual sync
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups
```
### Automated Off-Site Backups
Add to `/opt/nextcloud-stack/backup.sh` (after the cleanup section):
```bash
# Off-site backup (if rclone configured)
if command -v rclone &> /dev/null && rclone listremotes | grep -q "b2backup"; then
log "Syncing backups to off-site storage..."
rclone sync $BACKUP_DIR b2backup:nextcloud-backups --log-file=$LOG_FILE
log "Off-site sync completed"
fi
```
### Restore from Off-Site Backup
```bash
# List remote backups
rclone ls b2backup:nextcloud-backups/database/
# Download specific backup
rclone copy b2backup:nextcloud-backups/database/nextcloud_db_20260216_030000.sql.gz /opt/nextcloud-stack/backups/database/
# Download all backups
rclone sync b2backup:nextcloud-backups /opt/nextcloud-stack/backups/
```
---
## Backup Verification
### Test Database Backup Integrity
```bash
# 1. Extract backup
gunzip -c backups/database/nextcloud_db_20260216_030000.sql.gz > /tmp/test_restore.sql
# 2. Check for errors
grep -i "error" /tmp/test_restore.sql
# 3. Verify size (should be reasonable)
ls -lh /tmp/test_restore.sql
# 4. Cleanup
rm /tmp/test_restore.sql
```
### Test Volume Backup Integrity
```bash
# Test tar archive
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz | head -20
# Check for errors
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz > /dev/null && echo "Archive OK"
```
---
## Migration to New Server
**Complete migration guide:**
1. **Prepare new server:**
- Same or newer Ubuntu version
- Run Ansible setup playbooks (01-04)
2. **Backup old server:**
```bash
/opt/nextcloud-stack/backup.sh
```
3. **Copy backups to new server:**
```bash
rsync -avz old-server:/opt/nextcloud-stack/backups/ new-server:/opt/nextcloud-stack/backups/
```
4. **Update DNS:**
- Point all domains to new server IP
- Wait for propagation
5. **Restore on new server:**
- Follow "Complete System Restore" steps above
6. **Verify:**
- Test all services
- Check Nextcloud functionality
- Verify SSL certificates
7. **Decommission old server:**
- Only after confirming new server works
- Keep final backup from old server
---
## Backup Best Practices
### DO:
- ✅ Test restores regularly (monthly)
- ✅ Keep backups off-site (rclone to cloud storage)
- ✅ Verify backup integrity
- ✅ Monitor backup log for errors
- ✅ Document restore procedures
- ✅ Keep multiple backup generations
### DON'T:
- ❌ Store backups only on same server
- ❌ Assume backups work without testing
- ❌ Delete old backups without verification
- ❌ Skip database backups
- ❌ Ignore backup failure notifications
---
## Backup Schedule Customization
Edit `/opt/nextcloud-stack/backup.sh` to change:
- Backup frequency
- Retention period
- What gets backed up
- Compression settings
Edit cron job:
```bash
sudo crontab -e
# Change from 3:00 AM to 2:00 AM
0 2 * * * /opt/nextcloud-stack/backup.sh >> /opt/nextcloud-stack/backups/backup.log 2>&1
```
---
## Troubleshooting Backups
### Backup script fails
**Check logs:**
```bash
tail -100 /opt/nextcloud-stack/backups/backup.log
```
**Common issues:**
- Disk space full
- Docker container not running
- Permission denied
**Solutions:**
```bash
# Check disk space
df -h
# Check containers
docker ps
# Fix permissions
sudo chown -R root:root /opt/nextcloud-stack/backup.sh
sudo chmod +x /opt/nextcloud-stack/backup.sh
```
### Database backup empty
**Verify database is running:**
```bash
docker exec next-db pg_isready -U nextcloud
```
**Test manual backup:**
```bash
docker exec next-db pg_dump -U nextcloud nextcloud > /tmp/test.sql
ls -lh /tmp/test.sql
```
---
## Emergency Recovery
If all backups are lost and you need to start fresh:
```bash
# 1. Remove everything
ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
cd /opt/nextcloud-stack
docker compose down -v
# 2. Redeploy from scratch
ansible-playbook playbooks/site.yml --ask-vault-pass
# 3. Reconfigure Nextcloud manually
# Login and set up users, apps, etc.
```
---
**Remember:** Backups are worthless unless you've tested restoring from them!
**Last Updated:** 2026-02-16
+470
View File
@@ -0,0 +1,470 @@
# Quick Deployment Guide
Get your Nextcloud Stack running in under an hour!
## ⚡ Quick Start (3 Steps)
### Step 1: Configure (5-10 minutes)
```bash
./setup.sh
```
Answer the questions about your servers and preferences. This generates your encrypted configuration.
### Step 2: Deploy (30-45 minutes per server)
```bash
make deploy
```
Or manually:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
### Step 3: Access Your Services
- **Nextcloud**: `https://cloud.yourdomain.com`
- **OnlyOffice**: `https://office.yourdomain.com`
- **Excalidraw**: `https://draw.yourdomain.com`
- **Obsidian**: `https://notes.yourdomain.com`
**Management (Tailscale only)**:
- **Homarr Dashboard**: `https://home.yourdomain.com`
- **Dockhand**: `https://manage.yourdomain.com`
- **Uptime Kuma**: `https://uptime.yourdomain.com`
---
## 📋 Prerequisites Checklist
### Before You Begin
- [ ] Ubuntu 20.04+ server (LXC or VPS)
- [ ] Root or sudo access
- [ ] Minimum 100GB disk space
- [ ] Ansible 2.14+ on your control machine
- [ ] Domain name with DNS access
### DNS Configuration (CRITICAL!)
Before deployment, create these A records pointing to your server IP:
```
cloud.yourdomain.com → YOUR_SERVER_IP
office.yourdomain.com → YOUR_SERVER_IP
draw.yourdomain.com → YOUR_SERVER_IP
notes.yourdomain.com → YOUR_SERVER_IP
home.yourdomain.com → YOUR_SERVER_IP
manage.yourdomain.com → YOUR_SERVER_IP
uptime.yourdomain.com → YOUR_SERVER_IP
```
**Verify DNS before deployment:**
```bash
dig +short cloud.yourdomain.com
# Should return: YOUR_SERVER_IP
```
---
## 🎯 What Gets Deployed
### 12 Services
| Service | Purpose | Auto-Update |
|---------|---------|-------------|
| **Nextcloud** | File sync & collaboration | ❌ Manual |
| **PostgreSQL 18** | Database | ❌ Manual |
| **Redis 7** | Cache | ❌ Manual |
| **OnlyOffice** | Document editor | 👁️ Monitor |
| **Excalidraw** | Whiteboard | ✅ Auto |
| **Obsidian** | Notes | ✅ Auto |
| **Caddy** | Reverse proxy + SSL | ✅ Auto |
| **Homarr** | Dashboard | ✅ Auto |
| **Dockhand** | Container manager | ✅ Auto |
| **Uptime Kuma** | Monitoring | ✅ Auto |
| **Watchtower** | Auto-updater | N/A |
### Security Features
- ✅ UFW firewall configured
- ✅ Fail2ban for SSH protection
- ✅ Automatic SSL certificates (Let's Encrypt)
- ✅ Ansible Vault encrypted secrets
- ✅ Tailscale-only admin interfaces
- ✅ Automatic security updates
### Backup System
- **Daily**: Database backups (3:00 AM)
- **Weekly**: Volume backups (Sundays)
- **Retention**: 30 days
- **Location**: `/opt/nextcloud-stack/backups/`
---
## 📝 Detailed Setup Process
### 1. Install Ansible (Control Machine)
**Ubuntu/Debian:**
```bash
sudo apt update
sudo apt install ansible
```
**macOS:**
```bash
brew install ansible
```
**Verify:**
```bash
ansible --version
# Should be 2.14 or higher
```
### 2. Clone Repository
```bash
git clone <your-repo-url>
cd ansible-nextcloud-deployment
```
### 3. Run Interactive Setup
```bash
chmod +x setup.sh
./setup.sh
```
**You'll be asked for:**
1. **Server Details** (for each server):
- IP address
- Hostname
- Domain (e.g., `example.com`)
- SSH user
- SSH key path
2. **User Information**:
- Your name
- Your email (for SSL certificates)
- Timezone
3. **Admin Account**:
- Nextcloud admin username
- Nextcloud admin password
4. **Subdomain Preferences** (defaults shown):
- Nextcloud: `cloud`
- OnlyOffice: `office`
- Excalidraw: `draw`
- Obsidian: `notes`
- Homarr: `home`
- Dockhand: `manage`
- Uptime Kuma: `uptime`
5. **Tailscale** (optional):
- Auth key (or skip for manual activation)
6. **Monitoring**:
- Alert email
- Public status page (yes/no)
7. **Backups**:
- Install rclone (yes/no)
8. **Ansible Vault Password**:
- Create a password to encrypt secrets
**Output:**
- `inventory/hosts.yml` - Server inventory
- `inventory/group_vars/all/vars.yml` - Public variables
- `inventory/group_vars/all/vault.yml` - Encrypted secrets
### 4. Test Connectivity
```bash
make ping
```
Or:
```bash
ansible all -m ping --ask-vault-pass
```
Expected output:
```
server-hostname | SUCCESS => {
"ping": "pong"
}
```
### 5. Run Deployment
```bash
make deploy
```
Or manually:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
**What happens** (30-45 minutes):
1.**Preflight Checks** (2 min)
- Validates DNS, connectivity, disk space
2. ⚙️ **System Setup** (10-15 min)
- Installs packages
- Configures firewall (UFW)
- Sets up fail2ban
3. 🐳 **Docker Setup** (5 min)
- Removes old Docker
- Installs Docker CE + Compose v2
4. 🔐 **Tailscale** (2 min)
- Installs Tailscale
- Activates if auth key provided
5. 📦 **Deploy Stack** (10-15 min)
- Templates configuration files
- Pulls Docker images
- Starts all containers
- Initializes Nextcloud
6. 🌐 **Configure Caddy** (2-5 min)
- Validates configuration
- Obtains SSL certificates
7. 💾 **Setup Backups** (2 min)
- Creates backup script
- Sets up cron jobs
8.**Post-Deployment** (5 min)
- Verifies all services
- Installs Nextcloud apps
- Creates deployment report
### 6. Access Your Services
**Immediately after deployment:**
1. **Nextcloud**: `https://cloud.yourdomain.com`
- Login with admin credentials from setup
2. **Setup Uptime Kuma** (via Tailscale):
- `https://uptime.yourdomain.com`
- Create admin account on first visit
- Add monitors for your services
3. **Configure Homarr** (via Tailscale):
- `https://home.yourdomain.com`
- Add service tiles
- Customize dashboard
4. **OnlyOffice Integration**:
- Nextcloud → Settings → Administration → Office
- Document Server: `https://office.yourdomain.com`
---
## 🔧 Post-Deployment Tasks
### Activate Tailscale (if not done automatically)
```bash
ssh user@server
sudo tailscale up
```
### Test Backups
```bash
make backup
```
Or on the server:
```bash
ssh user@server
/opt/nextcloud-stack/backup.sh
```
### Check Service Status
```bash
make status
```
Or on the server:
```bash
ssh user@server
cd /opt/nextcloud-stack
docker compose ps
```
### View Logs
```bash
make logs
```
Or on the server:
```bash
ssh user@server
cd /opt/nextcloud-stack
docker compose logs -f
```
---
## 🎓 Learning Ansible Commands
### Useful Makefile Shortcuts
```bash
make help # Show all commands
make ping # Test connectivity
make check # Run preflight checks only
make deploy # Full deployment
make deploy-dry # Test without making changes
make status # Show container status
make logs # View all logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
```
### Direct Ansible Commands
```bash
# Run specific playbook
ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
# Run on specific server
ansible-playbook playbooks/site.yml --limit server1 --ask-vault-pass
# Dry run (no changes)
ansible-playbook playbooks/site.yml --ask-vault-pass --check
# View inventory
ansible-inventory --list
# Run ad-hoc command
ansible all -m shell -a "docker ps" --ask-vault-pass
```
---
## 🚨 Troubleshooting
### DNS Not Configured
**Symptom**: Let's Encrypt fails
**Solution**: Ensure all DNS A records point to server IP
```bash
dig +short cloud.yourdomain.com
```
### LXC Container Issues
**Symptom**: Docker won't start
**Solution** (on LXC host):
```bash
lxc config set CONTAINER_NAME security.nesting true
lxc restart CONTAINER_NAME
```
### Port Already in Use
**Symptom**: Caddy fails to start
**Solution**:
```bash
sudo systemctl stop apache2
sudo systemctl stop nginx
```
### Full troubleshooting guide:
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
---
## 📚 Additional Documentation
- [README.md](README.md) - Complete project overview
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions
- [BACKUP_RESTORE.md](BACKUP_RESTORE.md) - Backup and restore procedures
- [AGENTS.md](AGENTS.md) - Development guidelines
---
## 🔒 Security Best Practices
### After Deployment
1. **Change default passwords** (if you used simple ones during testing)
2. **Enable 2FA** in Nextcloud
3. **Review firewall rules**: `sudo ufw status`
4. **Check fail2ban**: `sudo fail2ban-client status sshd`
5. **Verify SSL certificates**: Visit all your domains in browser
6. **Test backups**: Ensure they're working
7. **Setup monitoring alerts** in Uptime Kuma
### Ongoing Maintenance
- **Weekly**: Check Uptime Kuma for service health
- **Monthly**: Test backup restore
- **Quarterly**: Review security updates
- **Annually**: Rotate passwords
---
## 🆘 Getting Help
1. **Check logs:**
```bash
docker compose logs [service-name]
```
2. **Deployment report:**
```bash
cat /opt/nextcloud-stack/DEPLOYMENT.txt
```
3. **System status:**
```bash
docker compose ps
sudo systemctl status docker
```
---
## ✅ Success Checklist
After deployment, verify:
- [ ] All containers running: `docker compose ps`
- [ ] Nextcloud accessible via HTTPS
- [ ] SSL certificate valid (green padlock in browser)
- [ ] Can login to Nextcloud
- [ ] OnlyOffice integration working
- [ ] Uptime Kuma showing all services green
- [ ] Homarr dashboard accessible
- [ ] Backup script runs successfully
- [ ] Tailscale connected (if using)
---
**You're all set! Enjoy your self-hosted Nextcloud stack! 🎉**
For questions or issues, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
**Last Updated:** 2026-02-16
+61
View File
@@ -0,0 +1,61 @@
# Makefile for Nextcloud Stack Deployment
# Provides convenient shortcuts for common Ansible operations
.PHONY: help setup ping check deploy deploy-stack deploy-dry update backup logs status restart rollback clean edit-vault rekey-vault
help: ## Show this help message
@echo "Nextcloud Stack Deployment - Make Commands"
@echo ""
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
setup: ## Run interactive setup script
@./setup.sh
ping: ## Test connectivity to all servers
@ansible all -m ping --ask-vault-pass
check: ## Run preflight checks only
@ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
deploy: ## Full deployment (all playbooks)
@ansible-playbook playbooks/site.yml --ask-vault-pass
deploy-stack: ## Deploy stack only (skip system setup)
@ansible-playbook playbooks/05-deploy-stack.yml --ask-vault-pass
deploy-dry: ## Dry run (check mode)
@ansible-playbook playbooks/site.yml --ask-vault-pass --check
update: ## Update Docker images (safe services only)
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose pull && docker compose up -d" --ask-vault-pass
backup: ## Run manual backup on all servers
@ansible all -m shell -a "/opt/nextcloud-stack/backup.sh" --ask-vault-pass
logs: ## Tail logs from all containers
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose logs -f --tail=100" --ask-vault-pass
status: ## Show container status
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose ps" --ask-vault-pass
restart: ## Restart all containers
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose restart" --ask-vault-pass
rollback: ## Emergency rollback
@ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
clean: ## Remove generated inventory (keeps vault)
@rm -f inventory/hosts.yml inventory/group_vars/all/vars.yml
@echo "Cleaned generated files (vault.yml preserved)"
edit-vault: ## Edit encrypted vault
@ansible-vault edit inventory/group_vars/all/vault.yml
rekey-vault: ## Change vault password
@ansible-vault rekey inventory/group_vars/all/vault.yml
inventory: ## Show inventory configuration
@ansible-inventory --list
graph: ## Show inventory graph
@ansible-inventory --graph
+560
View File
@@ -0,0 +1,560 @@
# Nextcloud Stack - Ansible Deployment Project
## 🎉 Project Complete!
This Ansible automation project provides complete, one-command deployment of a self-hosted Nextcloud productivity stack to Ubuntu-based LXC/VPS servers.
---
## 📦 What's Included
### Core Files
```
ansible-nextcloud-deployment/
├── setup.sh # ⭐ Interactive configuration wizard
├── Makefile # Convenient command shortcuts
├── ansible.cfg # Ansible configuration
├── .gitignore # Git ignore rules
├── inventory/ # Generated by setup.sh
│ ├── hosts.yml # Server inventory (generated)
│ └── group_vars/all/
│ ├── vars.yml # Public variables (generated)
│ └── vault.yml # Encrypted secrets (generated)
├── playbooks/ # ⭐ Deployment playbooks
│ ├── site.yml # Main orchestrator
│ ├── 01-preflight-checks.yml # DNS, connectivity validation
│ ├── 02-system-setup.yml # Packages, firewall, security
│ ├── 03-docker-setup.yml # Docker CE installation
│ ├── 04-tailscale-setup.yml # VPN setup
│ ├── 05-deploy-stack.yml # Docker Compose deployment
│ ├── 06-configure-caddy.yml # Reverse proxy + SSL
│ ├── 07-setup-backups.yml # Backup automation
│ ├── 08-post-deployment.yml # Final verification
│ └── 99-rollback.yml # Emergency rollback
├── roles/ # ⭐ Ansible roles
│ ├── nextcloud_stack/
│ │ └── templates/
│ │ ├── docker-compose.yml.j2 # Complete stack definition
│ │ └── env.j2 # Environment variables
│ └── caddy/
│ └── templates/
│ └── Caddyfile.j2 # Reverse proxy config
└── docs/ # ⭐ Documentation
├── README.md # Project overview
├── DEPLOYMENT_GUIDE.md # Quick start guide
├── TROUBLESHOOTING.md # Common issues & solutions
└── BACKUP_RESTORE.md # Backup procedures
```
### Stack Components (12 Services)
**Public Services** (via HTTPS):
- Nextcloud (file sync & collaboration)
- OnlyOffice (document editor)
- Excalidraw (whiteboard)
- Obsidian (note-taking)
**Infrastructure**:
- PostgreSQL 18 (database)
- Redis 7 (cache)
- Caddy (reverse proxy + automatic SSL)
**Management** (Tailscale-only):
- Homarr (dashboard)
- Dockhand (container manager)
- Uptime Kuma (monitoring)
**Automation**:
- Watchtower (auto-updates with safety exclusions)
---
## 🚀 Quick Start
### 1. Configure
```bash
./setup.sh
```
Interactive wizard collects all configuration:
- Server details (IP, domain, SSH)
- User information
- Admin credentials
- Subdomain preferences
- Tailscale auth key (optional)
- Monitoring settings
**Generates:**
- `inventory/hosts.yml` - Server inventory
- `inventory/group_vars/all/vars.yml` - Public config
- `inventory/group_vars/all/vault.yml` - Encrypted secrets
### 2. Deploy
```bash
make deploy
```
Or:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
**Duration:** 30-45 minutes per server
### 3. Access
- Nextcloud: `https://cloud.yourdomain.com`
- Homarr: `https://home.yourdomain.com` (Tailscale)
- Uptime Kuma: `https://uptime.yourdomain.com` (Tailscale)
---
## ✨ Key Features
### 🔒 Security
- **Firewall**: UFW configured (only SSH, HTTP, HTTPS, Tailscale open)
- **Fail2ban**: SSH brute-force protection (5 failures = 10 min ban)
- **Automatic Security Updates**: Unattended upgrades enabled
- **Secret Management**: Ansible Vault encryption for all credentials
- **Access Control**: Management UIs restricted to Tailscale network
- **SSL/TLS**: Automatic Let's Encrypt certificates via Caddy
- **Container Isolation**: Dedicated Docker network
### 💾 Backups
- **Daily**: PostgreSQL database dumps (3:00 AM)
- **Weekly**: Full volume backups (Sundays, 3:00 AM)
- **Retention**: 30 days
- **Location**: `/opt/nextcloud-stack/backups/`
- **Off-site Ready**: rclone pre-installed
- **Automated**: Cron jobs configured
- **Tested**: Documented restore procedures
### 🔄 Updates
**Automated (Watchtower)**:
- Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
**Monitor Only**:
- OnlyOffice (notifications, manual update)
**Manual Required**:
- Nextcloud, PostgreSQL, Redis (too critical to auto-update)
### 🌐 Multi-Server Support
Deploy identical stacks to multiple servers:
- Each server gets unique domain
- Fully independent deployments
- Shared Tailscale network (optional)
### 📊 Monitoring
- **Uptime Kuma**: Service health monitoring
- **Email Alerts**: Configurable notifications
- **Status Page**: Optional public status page
- **Container Health**: Docker healthchecks
- **SSL Monitoring**: Certificate expiry tracking
---
## 📚 Documentation
| Document | Purpose |
|----------|---------|
| [README.md](README.md) | Complete project overview, features, usage |
| [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) | Step-by-step deployment instructions |
| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | Common issues and solutions |
| [BACKUP_RESTORE.md](BACKUP_RESTORE.md) | Backup and restore procedures |
| [AGENTS.md](AGENTS.md) | Docker Compose & Ansible guidelines |
---
## 🛠️ Makefile Commands
```bash
make help # Show all commands
make setup # Run interactive setup
make ping # Test connectivity
make check # Run preflight checks
make deploy # Full deployment
make deploy-dry # Test run (no changes)
make status # Show container status
make logs # View all logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
make rekey-vault # Change vault password
```
---
## 🎯 Design Principles
### 1. **User-Friendly**
- Interactive setup script (no manual configuration editing)
- Automatic SSL certificates
- Clear documentation
- Helpful error messages
### 2. **Secure by Default**
- Minimal firewall rules
- Encrypted secrets (Ansible Vault)
- Tailscale-only admin interfaces
- Automatic security updates
- No hardcoded credentials
### 3. **Production-Ready**
- Automated backups
- Monitoring included
- Health checks configured
- Documented restore procedures
- Emergency rollback playbook
### 4. **Maintainable**
- Modular playbook structure
- Clear variable naming
- Comprehensive comments
- Role-based organization
- Git-friendly (.gitignore configured)
### 5. **Safe Updates**
- Critical services excluded from auto-update
- Watchtower uses label-based control
- Manual update procedures documented
- Rollback capability included
---
## 🔧 Deployment Flow
```
┌─────────────────────────────────────────────────────────────┐
│ 1. SETUP (./setup.sh) │
│ • Collect server details, domains, credentials │
│ • Generate encrypted inventory & variables │
│ • Create Ansible Vault with secrets │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. PREFLIGHT CHECKS (01-preflight-checks.yml) │
│ • Verify Ansible version, SSH connectivity │
│ • Check disk space, ports availability │
│ • Validate DNS records, detect LXC │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. SYSTEM SETUP (02-system-setup.yml) │
│ • Update packages, install essentials │
│ • Configure UFW firewall, fail2ban │
│ • Enable automatic security updates │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. DOCKER SETUP (03-docker-setup.yml) │
│ • Remove old Docker installations │
│ • Install Docker CE + Compose v2 │
│ • Configure Docker daemon │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. TAILSCALE SETUP (04-tailscale-setup.yml) │
│ • Install Tailscale │
│ • Activate if auth key provided │
│ • Enable systemd service │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. DEPLOY STACK (05-deploy-stack.yml) │
│ • Create directory structure │
│ • Template docker-compose.yml, .env, configs │
│ • Pull Docker images, start containers │
│ • Wait for services healthy │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. CONFIGURE CADDY (06-configure-caddy.yml) │
│ • Validate Caddyfile syntax │
│ • Reload Caddy configuration │
│ • Obtain Let's Encrypt SSL certificates │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 8. SETUP BACKUPS (07-setup-backups.yml) │
│ • Create backup script │
│ • Configure cron jobs (daily/weekly) │
│ • Run test backup │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 9. POST-DEPLOYMENT (08-post-deployment.yml) │
│ • Verify all containers running │
│ • Install Nextcloud apps │
│ • Configure background jobs │
│ • Create deployment report │
└─────────────────────────────────────────────────────────────┘
✅ DEPLOYMENT COMPLETE!
```
---
## 🧩 Template Files
### docker-compose.yml.j2
Complete Docker Compose stack with:
- All 12 services configured
- Health checks for critical services
- Proper dependencies (depends_on with conditions)
- Watchtower labels for update control
- Named volumes and networks
- Environment variables from .env
**Location**: `roles/nextcloud_stack/templates/docker-compose.yml.j2`
### Caddyfile.j2
Caddy reverse proxy configuration with:
- Automatic SSL for all domains
- Public services (Nextcloud, OnlyOffice, etc.)
- Tailscale-restricted services (Homarr, Dockhand, Uptime Kuma)
- Security headers
- Large file upload support (10GB)
- Optional public status page
**Location**: `roles/caddy/templates/Caddyfile.j2`
### env.j2
Environment variables template with:
- Database credentials
- Redis password
- Nextcloud admin credentials
- Application secrets (Homarr)
- Domain configuration
- Timezone settings
**Location**: `roles/nextcloud_stack/templates/env.j2`
---
## 🔐 Security Considerations
### Secrets Management
All sensitive data encrypted with Ansible Vault:
- Database passwords
- Admin credentials
- Application secrets
- Tailscale auth keys
**Never committed in plain text!**
### Network Security
- **Public**: Only Nextcloud and productivity apps
- **Tailscale-only**: All management interfaces
- **Internal**: Database and Redis (no external access)
### Firewall Rules (UFW)
```
22/tcp - SSH
80/tcp - HTTP (redirects to HTTPS)
443/tcp - HTTPS
443/udp - HTTP/3 (QUIC)
41641/udp - Tailscale
Default: DENY
```
### Update Strategy
**Why not auto-update everything?**
- **Nextcloud**: Database migrations may fail automatically
- **PostgreSQL**: Schema changes could break Nextcloud
- **Redis**: Unlikely issues, but excluded for consistency
**Safe to auto-update:**
- Stateless applications (Excalidraw, Obsidian)
- Infrastructure (Caddy)
- Management tools (Homarr, Dockhand)
---
## 📊 Resource Usage (Approximate)
### Disk Space
- Docker images: ~5GB
- Initial volumes: ~2GB
- Backups (30 days): ~50GB
- **Total recommended**: 100GB minimum
### Memory
- PostgreSQL: ~256MB
- Redis: ~256MB (configured limit)
- Nextcloud: ~512MB
- OnlyOffice: ~2GB
- Other services: ~1GB combined
- **Total**: 4-6GB RAM recommended
### Network
- **Inbound**: 80, 443 (HTTP/HTTPS), 41641 (Tailscale)
- **Outbound**: Internet access required for Let's Encrypt, Docker images, updates
---
## 🎓 Next Steps
### Immediate
1. **Test the deployment** on a test server first
2. **Verify DNS** is configured correctly
3. **Run setup.sh** and answer questions carefully
4. **Deploy** with `make deploy`
5. **Access Nextcloud** and complete initial setup
### After Deployment
1. **Configure Uptime Kuma** monitoring
2. **Customize Homarr** dashboard
3. **Test backups** with `make backup`
4. **Enable 2FA** in Nextcloud
5. **Install additional Nextcloud apps** as needed
### Ongoing
1. **Monitor** service health in Uptime Kuma
2. **Review backups** monthly
3. **Test restore** procedures quarterly
4. **Update Nextcloud** when new versions release (manually)
5. **Check logs** for any issues
---
## 💡 Tips & Best Practices
### For LXC Containers
**On LXC host, enable nested virtualization:**
```bash
lxc config set CONTAINER_NAME security.nesting true
lxc restart CONTAINER_NAME
```
### For Production Use
1. Use strong passwords (not example passwords)
2. Enable Nextcloud 2FA
3. Configure off-site backups with rclone
4. Set up Uptime Kuma email alerts
5. Review logs regularly
### For Multiple Servers
Each server can have:
- Different domain
- Different subdomain preferences
- Same or different Tailscale network
Just run `setup.sh` and add all servers when prompted.
---
## 🐛 Known Limitations
1. **Watchtower is archived** (December 2025)
- Still works, but no longer maintained
- Consider alternative update strategies long-term
2. **OnlyOffice needs 2GB+ RAM**
- May be slow on low-memory servers
- Consider disabling if not needed
3. **First-time deployment takes 30-45 min**
- Mostly Docker image downloads
- Subsequent deploys are faster
4. **Let's Encrypt rate limits**
- 50 certs/week per domain
- Use staging server for testing
---
## 📜 License
MIT License - Use freely, modify as needed
---
## 🙏 Acknowledgments
Built with these amazing open-source projects:
- **Nextcloud** - File sync and collaboration
- **Caddy** - Automatic HTTPS web server
- **Ansible** - IT automation
- **Docker** - Containerization
- **PostgreSQL** - Database
- **Tailscale** - Zero-config VPN
- **OnlyOffice**, **Excalidraw**, **Obsidian** - Productivity tools
- **Homarr**, **Dockhand**, **Uptime Kuma** - Management & monitoring
---
## 📞 Support
**Documentation:**
- [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) - Getting started
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Fix common issues
- [BACKUP_RESTORE.md](BACKUP_RESTORE.md) - Backup procedures
**Project Files:**
All source code, playbooks, and templates included in this repository.
---
## ✅ Checklist for New Users
Before deploying:
- [ ] Read DEPLOYMENT_GUIDE.md
- [ ] Install Ansible 2.14+ on control machine
- [ ] Have Ubuntu 20.04+ server ready
- [ ] Configure DNS A records
- [ ] Test SSH access to server
- [ ] Have domain name ready
- [ ] Decide on subdomain preferences
After deploying:
- [ ] Access Nextcloud web UI
- [ ] Complete Nextcloud setup wizard
- [ ] Setup Uptime Kuma monitoring
- [ ] Configure Homarr dashboard
- [ ] Test backup with `make backup`
- [ ] Enable Nextcloud 2FA
- [ ] Review deployment report
- [ ] Save Ansible Vault password securely
---
**Project Status**: ✅ Complete and Ready for Use
**Last Updated**: 2026-02-16
**Version**: 1.0
---
**Happy self-hosting! 🎉**
+382
View File
@@ -0,0 +1,382 @@
# Nextcloud Stack - Automated Deployment with Ansible
Complete automation for deploying a self-hosted Nextcloud productivity stack on Ubuntu-based LXC/VPS servers.
## Features
- **12 Services**: Nextcloud, OnlyOffice, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma, PostgreSQL, Redis, Caddy, Watchtower
- **Automatic SSL**: Let's Encrypt certificates via Caddy
- **Secure Management**: Tailscale-only access for admin interfaces
- **Auto-updates**: Watchtower with safety exclusions for critical services
- **Automated Backups**: Daily database backups, weekly volume backups (30-day retention)
- **Monitoring**: Uptime Kuma with configurable alerts
- **Multi-server**: Deploy to multiple VPS servers with unique domains
## Quick Start
### Prerequisites
1. **Control Machine** (your laptop):
- Ansible 2.14+ installed
- SSH access to target servers
2. **Target Server(s)**:
- Ubuntu 20.04+ (LXC container or VPS)
- Root or sudo access
- Minimum 100GB disk space
- Ports 80, 443 available
3. **DNS Configuration** (BEFORE deployment):
```
cloud.yourdomain.com → YOUR_SERVER_IP
office.yourdomain.com → YOUR_SERVER_IP
draw.yourdomain.com → YOUR_SERVER_IP
notes.yourdomain.com → YOUR_SERVER_IP
home.yourdomain.com → YOUR_SERVER_IP
manage.yourdomain.com → YOUR_SERVER_IP
uptime.yourdomain.com → YOUR_SERVER_IP
```
### Installation
1. **Clone this repository**:
```bash
git clone <your-repo-url>
cd ansible-nextcloud-deployment
```
2. **Run interactive setup**:
```bash
./setup.sh
```
This will ask for:
- Server IP addresses and domains
- Your name and email
- Admin credentials
- Subdomain preferences
- Tailscale auth key (optional)
- Monitoring email
- Ansible Vault password
3. **Deploy the stack**:
```bash
make deploy
```
Or manually:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
4. **Access your services**:
- Nextcloud: `https://cloud.yourdomain.com`
- OnlyOffice: `https://office.yourdomain.com`
- Homarr (Tailscale only): `https://home.yourdomain.com`
- Uptime Kuma (Tailscale only): `https://uptime.yourdomain.com`
## Project Structure
```
ansible-nextcloud-deployment/
├── setup.sh # Interactive configuration script
├── Makefile # Convenient command shortcuts
├── ansible.cfg # Ansible configuration
├── inventory/
│ ├── hosts.yml # Generated by setup.sh
│ └── group_vars/all/
│ ├── vars.yml # Public variables
│ └── vault.yml # Encrypted secrets
├── playbooks/
│ ├── site.yml # Main orchestrator
│ ├── 01-preflight-checks.yml # Pre-deployment validation
│ ├── 02-system-setup.yml # System packages & security
│ ├── 03-docker-setup.yml # Docker installation
│ ├── 04-tailscale-setup.yml # VPN setup
│ ├── 05-deploy-stack.yml # Docker Compose deployment
│ ├── 06-configure-caddy.yml # Reverse proxy configuration
│ ├── 07-setup-backups.yml # Backup automation
│ └── 08-post-deployment.yml # Final verification
└── roles/
├── nextcloud_stack/
│ └── templates/
│ ├── docker-compose.yml.j2
│ └── env.j2
└── caddy/
└── templates/
└── Caddyfile.j2
```
## Makefile Commands
```bash
make help # Show all commands
make setup # Run interactive setup
make ping # Test connectivity
make check # Run preflight checks
make deploy # Full deployment
make deploy-dry # Dry run (no changes)
make status # Show container status
make logs # View container logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
```
## Service Stack
| Service | Purpose | Port | Access | Auto-Update |
|---------|---------|------|--------|-------------|
| Caddy | Reverse proxy + SSL | 80, 443 | Public | ✅ |
| Nextcloud | File sync & collaboration | Internal | Public (via Caddy) | ❌ Manual |
| PostgreSQL 18 | Database | Internal | Internal only | ❌ Manual |
| Redis 7 | Cache layer | Internal | Internal only | ❌ Manual |
| OnlyOffice | Document editor | Internal | Public (via Caddy) | 👁️ Monitor |
| Excalidraw | Whiteboard tool | Internal | Public (via Caddy) | ✅ |
| Obsidian | Note-taking | Internal | Public (via Caddy) | ✅ |
| Homarr | Dashboard | Internal | Tailscale only | ✅ |
| Dockhand | Container manager | 3003 | Tailscale only | ✅ |
| Uptime Kuma | Monitoring | Internal | Tailscale only | ✅ |
| Watchtower | Auto-updater | N/A | N/A | N/A |
## Security Features
- **Firewall**: UFW configured with minimal open ports
- **Fail2ban**: SSH brute-force protection
- **Automatic Updates**: Unattended security updates enabled
- **Secret Management**: Ansible Vault encryption for all credentials
- **Access Control**: Management UIs restricted to Tailscale network
- **SSL/TLS**: Automatic Let's Encrypt certificates
- **Container Isolation**: Dedicated Docker network
## Backup System
- **Daily**: PostgreSQL database dumps (3:00 AM)
- **Weekly**: Full volume backups (Sundays)
- **Retention**: 30 days
- **Location**: `/opt/nextcloud-stack/backups/`
- **rclone**: Pre-installed for future remote backups
### Manual Backup
```bash
ssh user@server
cd /opt/nextcloud-stack
./backup.sh
```
### Restore from Backup
See [BACKUP_RESTORE.md](BACKUP_RESTORE.md) for detailed procedures.
## Tailscale Setup
If you didn't provide an auth key during setup:
```bash
ssh user@server
sudo tailscale up
```
Then access management interfaces via Tailscale IP or MagicDNS.
## Updating Services
### Safe to Auto-Update (Watchtower handles)
- Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
### Monitor Only (notifications, no auto-update)
- OnlyOffice
### Manual Update Required
- Nextcloud
- PostgreSQL
- Redis
### Updating Nextcloud Manually
```bash
ssh user@server
cd /opt/nextcloud-stack
# 1. Backup
./backup.sh
# 2. Enable maintenance mode
docker exec -u www-data next php occ maintenance:mode --on
# 3. Update
docker compose pull next
docker compose up -d next
# 4. Run database migrations
docker exec -u www-data next php occ upgrade
# 5. Disable maintenance mode
docker exec -u www-data next php occ maintenance:mode --off
```
## Troubleshooting
### DNS Not Configured
**Error**: Let's Encrypt fails to issue certificates
**Solution**: Ensure all DNS A records point to your server IP. Check with:
```bash
dig +short cloud.yourdomain.com
```
### LXC Container Issues
**Error**: Docker fails to start
**Solution**: On LXC host, enable nested virtualization:
```bash
lxc config set CONTAINER_NAME security.nesting true
lxc restart CONTAINER_NAME
```
### Port Already in Use
**Error**: Port 80 or 443 already bound
**Solution**: Check what's using the ports:
```bash
sudo ss -tlnp | grep ':80\|:443'
sudo systemctl stop apache2 # or nginx
```
### Nextcloud Stuck in Maintenance Mode
```bash
docker exec -u www-data next php occ maintenance:mode --off
```
### View Logs
```bash
cd /opt/nextcloud-stack
docker compose logs -f [service-name]
```
## Post-Deployment Tasks
1. **Login to Nextcloud**:
- URL: `https://cloud.yourdomain.com`
- Username: (set during setup)
- Password: (stored in vault)
2. **Setup Uptime Kuma**:
- URL: `https://uptime.yourdomain.com` (via Tailscale)
- Create admin account on first visit
- Configure monitors for your services
3. **Configure Homarr Dashboard**:
- URL: `https://home.yourdomain.com` (via Tailscale)
- Add service tiles
- Customize layout
4. **Configure OnlyOffice in Nextcloud**:
- Nextcloud Settings → Administration → Office
- Document Editing Service: `https://office.yourdomain.com`
5. **Test Backups**:
```bash
make backup
```
## File Locations on Server
```
/opt/nextcloud-stack/
├── docker-compose.yml
├── .env
├── backup.sh
├── configs/
│ ├── caddy/Caddyfile
│ └── nextcloud/
├── data/
│ ├── homarr/
│ └── obsidian/
└── backups/
├── database/
└── volumes/
```
## Environment Variables
All secrets are stored in `inventory/group_vars/all/vault.yml` (encrypted).
To edit:
```bash
make edit-vault
```
## Multiple Server Deployment
The setup script supports multiple servers. Each can have its own domain:
```bash
Server 1: 192.168.1.100 → cloud1.example.com
Server 2: 192.168.1.101 → cloud2.anotherdomain.net
Server 3: 192.168.1.102 → personal.mydomain.org
```
Deploy to all:
```bash
make deploy
```
Deploy to specific server:
```bash
ansible-playbook playbooks/site.yml --limit server_hostname --ask-vault-pass
```
## Maintenance
### Check Container Status
```bash
make status
```
### Restart Services
```bash
make restart
```
### Update Images (safe services only)
```bash
make update
```
### View Logs
```bash
make logs
```
## Uninstallation
```bash
ssh user@server
cd /opt/nextcloud-stack
docker compose down -v # WARNING: Deletes all data!
cd /opt
rm -rf nextcloud-stack
```
## Support
- See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for common issues
- See [BACKUP_RESTORE.md](BACKUP_RESTORE.md) for backup procedures
- See [AGENTS.md](AGENTS.md) for development guidelines
## License
MIT
## Acknowledgments
- Nextcloud team
- Caddy web server
- Ansible community
- All open-source contributors
---
**Built with ❤️ for self-hosting enthusiasts**
+192
View File
@@ -0,0 +1,192 @@
# 👋 Welcome to Nextcloud Stack Ansible Deployment!
## 🚀 Quick Start (Choose Your Path)
### 🏃 I Want to Deploy NOW
1. Read → [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) (10 min read)
2. Run → `./setup.sh` (5-10 min)
3. Deploy → `make deploy` (30-45 min)
### 📚 I Want to Learn More First
1. Read → [README.md](README.md) - Complete project overview
2. Read → [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) - Design & architecture
3. Then → Follow Quick Start above
### 🔧 I'm Having Issues
1. Check → [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
2. Check logs → `make logs`
3. Review → [BACKUP_RESTORE.md](BACKUP_RESTORE.md) if you need to restore
---
## 📖 Documentation Index
| Document | Purpose | Read Time |
|----------|---------|-----------|
| **[DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)** | Step-by-step deployment instructions | 10 min |
| **[README.md](README.md)** | Complete project overview & features | 15 min |
| **[PROJECT_SUMMARY.md](PROJECT_SUMMARY.md)** | Technical details & design | 10 min |
| **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** | Common issues & solutions | Reference |
| **[BACKUP_RESTORE.md](BACKUP_RESTORE.md)** | Backup & restore procedures | Reference |
| **[AGENTS.md](AGENTS.md)** | Docker & Ansible development guidelines | Reference |
---
## ✅ Pre-Deployment Checklist
Before you begin, ensure you have:
- [ ] Ubuntu 20.04+ server (LXC or VPS)
- [ ] Root or sudo access to server
- [ ] Minimum 100GB disk space on server
- [ ] Ansible 2.14+ installed on your laptop/control machine
- [ ] Domain name with DNS control
- [ ] SSH access configured to server
**Critical:** DNS A records must point to your server BEFORE deployment!
---
## 🎯 What You'll Get
After deployment, you'll have:
### Public Services (HTTPS)
- **Nextcloud** - File sync & collaboration
- **OnlyOffice** - Document editor (Word, Excel, PowerPoint)
- **Excalidraw** - Whiteboard & diagrams
- **Obsidian** - Note-taking & knowledge management
### Management Tools (Tailscale-only, secure)
- **Homarr** - Beautiful dashboard for all services
- **Dockhand** - Container management interface
- **Uptime Kuma** - Monitoring & alerts
### Infrastructure (automatic, invisible)
- **Caddy** - Reverse proxy with automatic SSL
- **PostgreSQL** - Database
- **Redis** - Cache for better performance
- **Watchtower** - Automatic updates (safe services only)
---
## 💡 Key Features
-**One-command deployment** - `make deploy` does everything
-**Automatic SSL** - Let's Encrypt certificates via Caddy
-**Secure by default** - Firewall, fail2ban, Vault encryption
-**Auto backups** - Daily DB, weekly volumes (30-day retention)
-**Multi-server** - Deploy to multiple VPS with unique domains
-**Production-ready** - Monitoring, backups, documentation
---
## 🏗️ Project Structure
```
/home/liph/programming/ansible/
├── setup.sh ⭐ # Start here! Interactive setup
├── Makefile # Convenient commands (make deploy)
├── playbooks/ # 10 Ansible playbooks
│ ├── site.yml # Main orchestrator
│ ├── 01-preflight-checks.yml # Validates environment
│ ├── 02-system-setup.yml # Packages & security
│ ├── 03-docker-setup.yml # Docker installation
│ ├── 04-tailscale-setup.yml # VPN setup
│ ├── 05-deploy-stack.yml # Main deployment
│ ├── 06-configure-caddy.yml # SSL & reverse proxy
│ ├── 07-setup-backups.yml # Backup automation
│ ├── 08-post-deployment.yml # Verification
│ └── 99-rollback.yml # Emergency rollback
├── roles/ # Templates & configs
│ ├── nextcloud_stack/
│ │ └── templates/
│ │ ├── docker-compose.yml.j2 # 12-service stack
│ │ └── env.j2 # Environment vars
│ └── caddy/
│ └── templates/
│ └── Caddyfile.j2 # Reverse proxy config
└── docs/ # You are here!
```
---
## 🎓 Common Commands
After setup, you'll use these frequently:
```bash
make deploy # Deploy everything
make status # Check container status
make logs # View all logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
```
---
## 📞 Need Help?
1. **Common Issues**: [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
2. **Backup/Restore**: [BACKUP_RESTORE.md](BACKUP_RESTORE.md)
3. **Full Documentation**: [README.md](README.md)
---
## ⚡ 30-Second Overview
This project automates deploying a complete self-hosted productivity stack:
1. **Configure once**: `./setup.sh` collects all info
2. **Deploy anywhere**: `make deploy` to any Ubuntu server
3. **Use immediately**: Access via `https://cloud.yourdomain.com`
**Time to deploy:** 30-45 minutes (mostly automatic)
---
## 🎯 Recommended First Steps
### For Complete Beginners
1. Read [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
2. Configure DNS for your domain
3. Run `./setup.sh` on your laptop
4. Run `make deploy` and wait
5. Access your new Nextcloud!
### For Experienced Users
1. Skim [README.md](README.md) to understand the stack
2. Review [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) for architecture
3. Run `./setup.sh` and `make deploy`
4. Review generated configs in `/opt/nextcloud-stack/`
### For Developers
1. Read [AGENTS.md](AGENTS.md) for development guidelines
2. Review playbooks in `playbooks/` directory
3. Check templates in `roles/*/templates/`
4. Customize as needed for your use case
---
## 🌟 What Makes This Special?
- **User-friendly**: Interactive setup, no manual config editing
- **Secure**: Ansible Vault, firewalls, Tailscale, automatic updates
- **Complete**: Monitoring, backups, SSL, documentation included
- **Production-ready**: Tested deployment procedures, rollback capability
- **Maintainable**: Clean code, comprehensive docs, modular design
---
## 🚦 Ready to Begin?
**New users**: Start with [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
**Quick reference**: See [README.md](README.md)
**Technical details**: Check [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md)
---
**Let's build your self-hosted cloud! 🚀**
+509
View File
@@ -0,0 +1,509 @@
# Troubleshooting Guide
Common issues and solutions for Nextcloud Stack deployment.
## Table of Contents
- [DNS Issues](#dns-issues)
- [SSL Certificate Problems](#ssl-certificate-problems)
- [Docker Issues](#docker-issues)
- [LXC Container Issues](#lxc-container-issues)
- [Nextcloud Issues](#nextcloud-issues)
- [Database Connection Issues](#database-connection-issues)
- [Tailscale Issues](#tailscale-issues)
- [Port Conflicts](#port-conflicts)
- [Permission Issues](#permission-issues)
---
## DNS Issues
### Problem: DNS records not resolving
**Symptoms:**
- Let's Encrypt fails to issue certificates
- Caddy shows certificate errors
- Services inaccessible via domain
**Diagnosis:**
```bash
dig +short cloud.yourdomain.com @8.8.8.8
```
**Solution:**
1. Ensure all required A records point to your server IP
2. Wait for DNS propagation (up to 48 hours, usually minutes)
3. Use [DNSChecker.org](https://dnschecker.org) to verify global propagation
**Required DNS Records:**
```
cloud.yourdomain.com → YOUR_SERVER_IP
office.yourdomain.com → YOUR_SERVER_IP
draw.yourdomain.com → YOUR_SERVER_IP
notes.yourdomain.com → YOUR_SERVER_IP
home.yourdomain.com → YOUR_SERVER_IP
manage.yourdomain.com → YOUR_SERVER_IP
uptime.yourdomain.com → YOUR_SERVER_IP
```
**Temporary Workaround:**
Edit `/etc/hosts` on your local machine:
```
YOUR_SERVER_IP cloud.yourdomain.com
```
---
## SSL Certificate Problems
### Problem: Let's Encrypt rate limit exceeded
**Symptoms:**
- Error: "too many certificates already issued"
**Solution:**
1. Use Let's Encrypt staging server for testing
2. Edit Caddyfile (add to global options):
```caddy
{
email {{ user_email }}
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
```
3. Reload Caddy: `docker exec caddy caddy reload`
4. After testing, remove staging server line
**Rate Limits:**
- 50 certificates per domain per week
- 5 duplicate certificates per week
### Problem: Certificate validation failed
**Symptoms:**
- "Failed to verify" errors in Caddy logs
**Diagnosis:**
```bash
docker logs caddy
```
**Common Causes:**
1. DNS not pointing to server
2. Firewall blocking port 80/443
3. Another service using port 80/443
**Solution:**
```bash
# Check firewall
sudo ufw status
# Check port usage
sudo ss -tlnp | grep ':80\|:443'
# Check DNS
dig +short yourdomain.com
```
---
## Docker Issues
### Problem: Docker daemon won't start
**Symptoms:**
- `docker ps` fails
- Error: "Cannot connect to Docker daemon"
**Diagnosis:**
```bash
sudo systemctl status docker
sudo journalctl -xu docker
```
**Solution:**
```bash
sudo systemctl restart docker
```
### Problem: Containers keep restarting
**Diagnosis:**
```bash
cd /opt/nextcloud-stack
docker compose logs [service-name]
```
**Common Causes:**
1. Configuration errors
2. Port conflicts
3. Missing dependencies
**Solution:**
```bash
# Check specific container
docker logs next-db
docker logs next
docker logs caddy
# Restart specific service
docker compose restart next
```
---
## LXC Container Issues
### Problem: Docker fails to start in LXC
**Symptoms:**
- Error: "cgroups: cgroup mountpoint does not exist"
- Docker daemon fails to start
**Diagnosis:**
```bash
systemd-detect-virt # Should show "lxc"
```
**Solution on LXC Host:**
```bash
# Set security nesting
lxc config set CONTAINER_NAME security.nesting true
# May also need privileged mode
lxc config set CONTAINER_NAME security.privileged true
# Restart container
lxc restart CONTAINER_NAME
```
**Inside LXC Container:**
```bash
# Verify cgroups
mount | grep cgroup
# Check Docker status
sudo systemctl status docker
```
### Problem: AppArmor denials in LXC
**Solution on LXC Host:**
```bash
lxc config set CONTAINER_NAME raw.lxc "lxc.apparmor.profile=unconfined"
lxc restart CONTAINER_NAME
```
---
## Nextcloud Issues
### Problem: Nextcloud stuck in maintenance mode
**Symptoms:**
- Web interface shows "System in maintenance mode"
**Solution:**
```bash
docker exec -u www-data next php occ maintenance:mode --off
```
### Problem: Trusted domain error
**Symptoms:**
- "Access through untrusted domain" error
**Solution:**
```bash
docker exec -u www-data next php occ config:system:set trusted_domains 1 --value=cloud.yourdomain.com
```
### Problem: Redis connection failed
**Diagnosis:**
```bash
docker logs next-redis
docker exec next-redis redis-cli ping
```
**Solution:**
```bash
# Reconfigure Redis in Nextcloud
docker exec -u www-data next php occ config:system:set redis host --value=next-redis
docker exec -u www-data next php occ config:system:set redis port --value=6379
```
### Problem: File uploads fail
**Symptoms:**
- Large files won't upload
- Error 413 (Payload Too Large)
**Solution:**
Already configured in Caddyfile for 10GB uploads. Check:
```bash
docker exec -u www-data next php occ config:system:get max_upload
```
### Problem: OnlyOffice integration not working
**Solution:**
```bash
# Install OnlyOffice app
docker exec -u www-data next php occ app:install onlyoffice
# Configure document server URL
docker exec -u www-data next php occ config:app:set onlyoffice DocumentServerUrl --value="https://office.yourdomain.com/"
# Disable JWT (or configure if needed)
docker exec -u www-data next php occ config:app:set onlyoffice jwt_secret --value=""
```
---
## Database Connection Issues
### Problem: Nextcloud can't connect to database
**Symptoms:**
- Error: "SQLSTATE[08006]"
- Nextcloud shows database error
**Diagnosis:**
```bash
# Check if PostgreSQL is running
docker ps | grep next-db
# Check PostgreSQL logs
docker logs next-db
# Test connection
docker exec next-db pg_isready -U nextcloud
```
**Solution:**
```bash
# Restart database
docker compose restart next-db
# Wait for it to be healthy
docker exec next-db pg_isready -U nextcloud
# Restart Nextcloud
docker compose restart next
```
### Problem: Database initialization failed
**Symptoms:**
- PostgreSQL container keeps restarting
- Empty database
**Solution:**
```bash
# Remove volumes and recreate
cd /opt/nextcloud-stack
docker compose down -v
docker compose up -d
```
**⚠️ WARNING:** This deletes all data! Only use for fresh installations.
---
## Tailscale Issues
### Problem: Can't access Tailscale-only services
**Symptoms:**
- Homarr, Dockhand, Uptime Kuma return 403 Forbidden
**Diagnosis:**
```bash
# Check if Tailscale is running
sudo tailscale status
# Get Tailscale IP
tailscale ip -4
```
**Solution:**
```bash
# Activate Tailscale (if not done)
sudo tailscale up
# Verify connection
tailscale status
```
**Access via:**
- Tailscale IP: `https://100.64.x.x:PORT`
- MagicDNS: `https://hostname.tailnet-name.ts.net`
### Problem: Tailscale not installed
**Solution:**
```bash
# Re-run Tailscale playbook
ansible-playbook playbooks/04-tailscale-setup.yml --ask-vault-pass
```
---
## Port Conflicts
### Problem: Port 80 or 443 already in use
**Symptoms:**
- Error: "bind: address already in use"
- Caddy won't start
**Diagnosis:**
```bash
sudo ss -tlnp | grep ':80\|:443'
```
**Common Culprits:**
- Apache2
- Nginx
- Another Caddy instance
**Solution:**
```bash
# Stop conflicting service
sudo systemctl stop apache2
sudo systemctl disable apache2
# OR
sudo systemctl stop nginx
sudo systemctl disable nginx
# Restart Caddy
docker compose restart caddy
```
---
## Permission Issues
### Problem: Permission denied errors in Nextcloud
**Symptoms:**
- Can't upload files
- Can't install apps
**Diagnosis:**
```bash
# Check file permissions
docker exec next ls -la /var/www/html
```
**Solution:**
```bash
# Fix permissions (run inside container)
docker exec next chown -R www-data:www-data /var/www/html
```
### Problem: Docker socket permission denied
**Symptoms:**
- Homarr or Dockhand can't see containers
**Solution:**
Docker socket is mounted read-only by design for security.
This is normal and expected.
---
## Emergency Commands
### Completely restart the stack
```bash
cd /opt/nextcloud-stack
docker compose down
docker compose up -d
```
### View all logs in real-time
```bash
cd /opt/nextcloud-stack
docker compose logs -f
```
### Check container health
```bash
docker compose ps
docker inspect --format='{{.State.Health.Status}}' next
```
### Rebuild a specific container
```bash
docker compose up -d --force-recreate --no-deps next
```
### Emergency backup
```bash
/opt/nextcloud-stack/backup.sh
```
### Reset Nextcloud admin password
```bash
docker exec -u www-data next php occ user:resetpassword admin
```
---
## Getting Help
If none of these solutions work:
1. **Check logs:**
```bash
docker compose logs [service-name]
```
2. **Check system logs:**
```bash
sudo journalctl -xe
```
3. **Verify configuration:**
```bash
cat /opt/nextcloud-stack/docker-compose.yml
cat /opt/nextcloud-stack/.env
```
4. **Test connectivity:**
```bash
curl -I https://cloud.yourdomain.com
docker exec caddy caddy validate
```
5. **Deployment report:**
```bash
cat /opt/nextcloud-stack/DEPLOYMENT.txt
```
---
## Recovery Procedures
### Restore from backup
See [BACKUP_RESTORE.md](BACKUP_RESTORE.md)
### Complete reinstallation
```bash
# 1. Backup first!
/opt/nextcloud-stack/backup.sh
# 2. Remove deployment
ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
# 3. Redeploy
ansible-playbook playbooks/site.yml --ask-vault-pass
```
---
**Last Updated:** 2026-02-16
+35
View File
@@ -0,0 +1,35 @@
# Ansible Configuration File
# Generated for Nextcloud Stack Deployment
[defaults]
inventory = inventory/hosts.yml
roles_path = roles
host_key_checking = False
retry_files_enabled = False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
stdout_callback = yaml
callbacks_enabled = profile_tasks, timer
deprecation_warnings = False
# SSH settings
timeout = 30
forks = 5
# Privilege escalation
become = True
become_method = sudo
become_ask_pass = False
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
+7
View File
@@ -0,0 +1,7 @@
DB_PASSWORD=
DB_USERNAME=
DB_DATABASE_NAME=
DB_HOST=
PUID=33
PGID=1000
+108
View File
@@ -0,0 +1,108 @@
services:
excalidraw:
container_name: excalidraw
image: docker.io/excalidraw/excalidraw:latest
restart: unless-stopped
ports:
- "3009:80"
environment:
- NODE_ENV=production
- TZ=Europe/Zurich
# Database (PostgreSQL)
next-db:
image: docker.io/postgres:18
container_name: next-db
environment:
- POSTGRES_DB=${DB_DATABASE_NAME}
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
-
restart: unless-stopped
networks:
- nextcloud_network
# # Redis Cache
# next_redis:
# image: docker.io/redis:latest
# container_name: next-redis
# command: redis-server --save 60 1 --loglevel warning
# volumes:
# - ./data/redis:/data
# restart: unless-stopped
# networks:
# - nextcloud_network
# Nextcloud Main Application
next:
image: docker.io/nextcloud:latest
container_name: next
depends_on:
- next-db
mem_swappiness: -1
ports:
- "8808:80"
environment:
- POSTGRES_DB=${DB_DATABASE_NAME}
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_HOST=${DB_HOST}
- NEXTCLOUD_TRUSTED_DOMAINS=next.liphlink.xyz
- NEXTCLOUD_ADMIN_USER=liph
- NEXTCLOUD_ADMIN_PASSWORD=1ChagenexT
volumes:
- :/var/www/html
- :/var/www/html/config:Z
- :/var/www/html/custom_apps
restart: unless-stopped
networks:
- nextcloud_network
# OnlyOffice (alternative to Collabora)
onlyoffice:
image: docker.io/onlyoffice/documentserver:latest
container_name: onlyoffice
ports:
- 8000:80
environment:
# - JWT_SECRET='S2jaRmOxSxnFoOMhzXotL1QTptDuqhom'
- JWT_ENABLED=false
- JWT_HEADER=Authorization
- JWT_IN_BODY=true
volumes:
- :/var/www/onlyoffice/Data
restart: unless-stopped
networks:
- nextcloud_network
obsidian:
image: lscr.io/linuxserver/obsidian:latest
container_name: obsidian
security_opt:
- seccomp:unconfined #optional
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- :/config:z
- :/vault:z
ports:
- 3005:3000
- 3004:3001
shm_size: "1gb"
restart: unless-stopped
volumes:
pg_data:
redis_data:
nextcloud_data:
nextcloud_config:
nextcloud_apps:
nextcloud_db_data:
onlyoffice_data:
networks:
nextcloud_network:
name: nextcloud_network
+98
View File
@@ -0,0 +1,98 @@
---
# Playbook 01: Preflight Checks
# Validates environment before deployment
- name: Preflight Checks
hosts: all
gather_facts: yes
become: no
tasks:
- name: Check Ansible version
assert:
that:
- ansible_version.full is version('2.14', '>=')
fail_msg: "Ansible 2.14 or higher is required"
success_msg: "Ansible version OK ({{ ansible_version.full }})"
delegate_to: localhost
run_once: true
- name: Test SSH connectivity
ping:
- name: Check sudo access
command: sudo -n true
changed_when: false
- name: Check Python3 availability
command: python3 --version
register: python_version
changed_when: false
- name: Display Python version
debug:
msg: "Python version: {{ python_version.stdout }}"
- name: Check disk space
shell: df -h / | awk 'NR==2 {print $4}'
register: disk_space
changed_when: false
- name: Validate sufficient disk space
assert:
that:
- disk_space.stdout is regex('[0-9]+G')
fail_msg: "Insufficient disk space. At least 20GB recommended."
success_msg: "Disk space OK ({{ disk_space.stdout }} available)"
- name: Check if ports 80 and 443 are available
wait_for:
port: "{{ item }}"
state: stopped
timeout: 1
loop:
- 80
- 443
ignore_errors: yes
register: port_check
- name: Detect virtualization type
command: systemd-detect-virt
register: virt_type
changed_when: false
failed_when: false
- name: Warn if running in LXC
debug:
msg: |
⚠️ RUNNING IN LXC CONTAINER
Docker requires nested virtualization.
Ensure on LXC host: lxc config set {{ inventory_hostname }} security.nesting true
when: "'lxc' in virt_type.stdout"
- name: Validate DNS resolution for all subdomains
command: dig +short {{ item }}.{{ domain }} @8.8.8.8
register: dns_check
changed_when: false
failed_when: false
loop:
- "{{ subdomain_nextcloud }}"
- "{{ subdomain_office }}"
- "{{ subdomain_draw }}"
- "{{ subdomain_notes }}"
- "{{ subdomain_homarr }}"
- "{{ subdomain_dockhand }}"
- "{{ subdomain_uptime }}"
- name: Display DNS check results
debug:
msg: "{{ item.item }}.{{ domain }} → {{ item.stdout if item.stdout else 'NOT CONFIGURED' }}"
loop: "{{ dns_check.results }}"
loop_control:
label: "{{ item.item }}.{{ domain }}"
- name: Preflight checks complete
debug:
msg: |
✓ All preflight checks passed
✓ Ready to proceed with deployment
+119
View File
@@ -0,0 +1,119 @@
---
# Playbook 02: System Setup
# Install packages, configure firewall and security
- name: System Setup
hosts: all
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Upgrade all packages
apt:
upgrade: dist
autoremove: yes
autoclean: yes
- name: Install essential packages
apt:
name:
- curl
- wget
- git
- vim
- htop
- net-tools
- dnsutils
- ufw
- fail2ban
- unattended-upgrades
- apt-transport-https
- ca-certificates
- gnupg
- lsb-release
- software-properties-common
- python3-pip
- python3-docker
- rsync
- "{% if install_rclone %}rclone{% endif %}"
state: present
- name: Configure UFW - Allow SSH
ufw:
rule: allow
port: '22'
proto: tcp
- name: Configure UFW - Allow HTTP
ufw:
rule: allow
port: '80'
proto: tcp
- name: Configure UFW - Allow HTTPS TCP
ufw:
rule: allow
port: '443'
proto: tcp
- name: Configure UFW - Allow HTTPS UDP (HTTP/3)
ufw:
rule: allow
port: '443'
proto: udp
- name: Configure UFW - Allow Tailscale
ufw:
rule: allow
port: '41641'
proto: udp
- name: Enable UFW
ufw:
state: enabled
policy: deny
- name: Configure fail2ban for SSH
copy:
dest: /etc/fail2ban/jail.local
content: |
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 600
notify: restart fail2ban
- name: Enable unattended security updates
copy:
dest: /etc/apt/apt.conf.d/20auto-upgrades
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
- name: Set timezone
timezone:
name: "{{ timezone }}"
- name: Set kernel parameters for Docker
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
reload: yes
loop:
- { key: 'net.ipv4.ip_forward', value: '1' }
- { key: 'fs.inotify.max_user_watches', value: '524288' }
handlers:
- name: restart fail2ban
service:
name: fail2ban
state: restarted
+115
View File
@@ -0,0 +1,115 @@
---
# Playbook 03: Docker Setup
# Install Docker CE and Docker Compose v2
- name: Docker Installation
hosts: all
become: yes
tasks:
- name: Remove old Docker installations
apt:
name:
- docker
- docker-engine
- docker.io
- containerd
- runc
- docker-compose
state: absent
purge: yes
- name: Remove old Docker data directories
file:
path: "{{ item }}"
state: absent
loop:
- /var/lib/docker
- /var/lib/containerd
- /etc/docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker CE
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: yes
- name: Create Docker daemon configuration
copy:
dest: /etc/docker/daemon.json
content: |
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"userland-proxy": false,
"live-restore": true
}
notify: restart docker
- name: Create docker group
group:
name: docker
state: present
- name: Add user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Create deployment directory
file:
path: "{{ deployment_dir }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: Start and enable Docker
service:
name: docker
state: started
enabled: yes
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
- name: Display Docker version
debug:
msg: "Docker installed: {{ docker_version.stdout }}"
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
- name: Display Docker Compose version
debug:
msg: "Docker Compose installed: {{ compose_version.stdout }}"
handlers:
- name: restart docker
service:
name: docker
state: restarted
+60
View File
@@ -0,0 +1,60 @@
---
# Playbook 04: Tailscale Setup
# Install and optionally activate Tailscale VPN
- name: Tailscale Installation
hosts: all
become: yes
tasks:
- name: Add Tailscale GPG key
apt_key:
url: https://pkgs.tailscale.com/stable/ubuntu/{{ ansible_distribution_release }}.noarmor.gpg
state: present
- name: Add Tailscale repository
apt_repository:
repo: "deb https://pkgs.tailscale.com/stable/ubuntu {{ ansible_distribution_release }} main"
state: present
- name: Install Tailscale
apt:
name: tailscale
state: present
update_cache: yes
- name: Check if Tailscale auth key is provided
set_fact:
tailscale_auto_enable: "{{ tailscale_auth_key is defined and tailscale_auth_key != '' }}"
- name: Activate Tailscale (if auth key provided)
command: tailscale up --authkey={{ tailscale_auth_key }} --advertise-tags=tag:nextcloud
when: tailscale_auto_enable
register: tailscale_activation
- name: Get Tailscale IP (if activated)
command: tailscale ip -4
register: tailscale_ip
when: tailscale_auto_enable
changed_when: false
- name: Display Tailscale status (activated)
debug:
msg: |
✓ Tailscale activated
IP: {{ tailscale_ip.stdout }}
when: tailscale_auto_enable
- name: Display manual activation instructions (not activated)
debug:
msg: |
Tailscale installed but not activated.
To enable, run on the server:
sudo tailscale up
when: not tailscale_auto_enable
- name: Enable Tailscale service
service:
name: tailscaled
state: started
enabled: yes
+109
View File
@@ -0,0 +1,109 @@
---
# Playbook 05: Deploy Stack
# Deploy Docker Compose stack with all services
- name: Deploy Nextcloud Stack
hosts: all
become: yes
tasks:
- name: Create directory structure
file:
path: "{{ item }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
loop:
- "{{ deployment_dir }}/configs"
- "{{ deployment_dir }}/configs/caddy"
- "{{ deployment_dir }}/configs/nextcloud"
- "{{ deployment_dir }}/data"
- "{{ deployment_dir }}/data/homarr"
- "{{ deployment_dir }}/data/obsidian/config"
- "{{ deployment_dir }}/data/obsidian/vault"
- "{{ deployment_dir }}/backups"
- "{{ deployment_dir }}/backups/database"
- "{{ deployment_dir }}/backups/volumes"
- name: Template docker-compose.yml
template:
src: ../roles/nextcloud_stack/templates/docker-compose.yml.j2
dest: "{{ deployment_dir }}/docker-compose.yml"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
- name: Template .env file
template:
src: ../roles/nextcloud_stack/templates/env.j2
dest: "{{ deployment_dir }}/.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0600'
no_log: true
- name: Template Caddyfile
template:
src: ../roles/caddy/templates/Caddyfile.j2
dest: "{{ deployment_dir }}/configs/caddy/Caddyfile"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
- name: Pull Docker images
command: docker compose pull
args:
chdir: "{{ deployment_dir }}"
become_user: "{{ ansible_user }}"
- name: Start Docker Compose stack
command: docker compose up -d
args:
chdir: "{{ deployment_dir }}"
become_user: "{{ ansible_user }}"
- name: Wait for PostgreSQL to be healthy
command: docker exec next-db pg_isready -U {{ db_user }}
register: pg_ready
until: pg_ready.rc == 0
retries: 30
delay: 10
changed_when: false
- name: Wait for Nextcloud to be accessible
uri:
url: http://localhost:8808/status.php
status_code: 200
register: nc_status
until: nc_status.status == 200
retries: 30
delay: 10
- name: Configure Nextcloud Redis cache
command: |
docker exec -u www-data next php occ config:system:set redis host --value=next-redis
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Configure Nextcloud Redis port
command: |
docker exec -u www-data next php occ config:system:set redis port --value=6379
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Configure Nextcloud memcache
command: |
docker exec -u www-data next php occ config:system:set memcache.locking --value='\\OC\\Memcache\\Redis'
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Display deployment success
debug:
msg: |
✓ Docker Compose stack deployed
✓ All containers started
✓ Nextcloud is accessible
+48
View File
@@ -0,0 +1,48 @@
---
# Playbook 06: Configure Caddy
# Setup reverse proxy and obtain SSL certificates
- name: Configure Caddy Reverse Proxy
hosts: all
become: yes
tasks:
- name: Validate Caddyfile syntax
command: docker exec caddy caddy validate --config /etc/caddy/Caddyfile
args:
chdir: "{{ deployment_dir }}"
register: caddy_validate
failed_when: caddy_validate.rc != 0
changed_when: false
- name: Reload Caddy configuration
command: docker exec caddy caddy reload --config /etc/caddy/Caddyfile
args:
chdir: "{{ deployment_dir }}"
- name: Wait for SSL certificates (may take 1-2 minutes)
pause:
seconds: 30
prompt: "Waiting for Let's Encrypt to issue certificates..."
- name: Test HTTPS endpoint for Nextcloud
uri:
url: "https://{{ subdomain_nextcloud }}.{{ domain }}/status.php"
validate_certs: yes
status_code: 200
register: https_test
until: https_test.status == 200
retries: 10
delay: 10
ignore_errors: yes
- name: Display Caddy status
debug:
msg: |
✓ Caddyfile validated
✓ Caddy reloaded
{% if https_test.status == 200 %}
✓ HTTPS working: https://{{ subdomain_nextcloud }}.{{ domain }}
{% else %}
⚠ HTTPS check failed - verify DNS and firewall
{% endif %}
+87
View File
@@ -0,0 +1,87 @@
---
# Playbook 07: Setup Backups
# Configure automated backup system
- name: Setup Backup System
hosts: all
become: yes
tasks:
- name: Create backup script
copy:
dest: "{{ deployment_dir }}/backup.sh"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
content: |
#!/bin/bash
# Nextcloud Stack Backup Script
set -euo pipefail
BACKUP_DIR="{{ deployment_dir }}/backups"
DB_BACKUP_DIR="$BACKUP_DIR/database"
VOLUME_BACKUP_DIR="$BACKUP_DIR/volumes"
RETENTION_DAYS={{ backup_retention_days }}
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="$BACKUP_DIR/backup.log"
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "Starting backup process..."
# Database backup
log "Backing up PostgreSQL database..."
docker exec next-db pg_dump -U {{ db_user }} {{ db_name }} | \
gzip > "$DB_BACKUP_DIR/nextcloud_db_$DATE.sql.gz"
if [ $? -eq 0 ]; then
log "Database backup completed: nextcloud_db_$DATE.sql.gz"
else
log "ERROR: Database backup failed!"
exit 1
fi
# Weekly volume backup (Sundays only)
if [ $(date +%u) -eq 7 ]; then
log "Weekly volume backup (Sunday)..."
docker exec -u www-data next php occ maintenance:mode --on
tar -czf "$VOLUME_BACKUP_DIR/nextcloud_data_$DATE.tar.gz" \
-C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data . 2>/dev/null || true
tar -czf "$VOLUME_BACKUP_DIR/configs_$DATE.tar.gz" \
-C {{ deployment_dir }}/configs . 2>/dev/null || true
docker exec -u www-data next php occ maintenance:mode --off
log "Volume backup completed"
fi
# Cleanup old backups
log "Cleaning up backups older than $RETENTION_DAYS days..."
find "$DB_BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
find "$VOLUME_BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
log "Backup process completed successfully!"
- name: Create cron job for daily backups
cron:
name: "Nextcloud Stack Backup"
minute: "0"
hour: "3"
job: "{{ deployment_dir }}/backup.sh >> {{ deployment_dir }}/backups/backup.log 2>&1"
user: root
- name: Run initial test backup
command: "{{ deployment_dir }}/backup.sh"
register: backup_test
ignore_errors: yes
- name: Display backup status
debug:
msg: |
✓ Backup script created
✓ Cron job configured (daily 3:00 AM)
✓ Test backup completed
Location: {{ deployment_dir }}/backups/
+154
View File
@@ -0,0 +1,154 @@
---
# Playbook 08: Post-Deployment
# Final verification and configuration
- name: Post-Deployment Tasks
hosts: all
become: yes
tasks:
- name: Verify all containers are running
command: docker compose ps --format json
args:
chdir: "{{ deployment_dir }}"
register: container_status
changed_when: false
- name: Check Nextcloud status
command: docker exec -u www-data next php occ status
args:
chdir: "{{ deployment_dir }}"
register: nc_status
changed_when: false
ignore_errors: yes
- name: Install recommended Nextcloud apps
command: docker exec -u www-data next php occ app:install {{ item }}
args:
chdir: "{{ deployment_dir }}"
loop:
- calendar
- contacts
- tasks
- notes
ignore_errors: yes
- name: Configure Nextcloud background jobs
command: docker exec -u www-data next php occ background:cron
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Setup Nextcloud cron job
cron:
name: "Nextcloud Background Jobs"
minute: "*/5"
job: "docker exec -u www-data next php /var/www/html/cron.php"
user: root
- name: Get Tailscale IP (if activated)
command: tailscale ip -4
register: tailscale_ip_result
changed_when: false
failed_when: false
- name: Create deployment report
copy:
dest: "{{ deployment_dir }}/DEPLOYMENT.txt"
content: |
════════════════════════════════════════════════════════════
NEXTCLOUD STACK DEPLOYMENT REPORT
════════════════════════════════════════════════════════════
Server: {{ inventory_hostname }}
IP Address: {{ ansible_host }}
Domain: {{ domain }}
Deployment Date: {{ ansible_date_time.iso8601 }}
────────────────────────────────────────────────────────────
PUBLIC SERVICES (HTTPS)
────────────────────────────────────────────────────────────
• Nextcloud: https://{{ subdomain_nextcloud }}.{{ domain }}
• OnlyOffice: https://{{ subdomain_office }}.{{ domain }}
• Excalidraw: https://{{ subdomain_draw }}.{{ domain }}
• Obsidian: https://{{ subdomain_notes }}.{{ domain }}
────────────────────────────────────────────────────────────
MANAGEMENT SERVICES (Tailscale only)
────────────────────────────────────────────────────────────
• Homarr: https://{{ subdomain_homarr }}.{{ domain }}
• Dockhand: https://{{ subdomain_dockhand }}.{{ domain }}
• Uptime Kuma: https://{{ subdomain_uptime }}.{{ domain }}
────────────────────────────────────────────────────────────
CREDENTIALS
────────────────────────────────────────────────────────────
Nextcloud Admin User: {{ admin_user }}
Nextcloud Admin Password: [stored in Ansible vault]
────────────────────────────────────────────────────────────
TAILSCALE
────────────────────────────────────────────────────────────
{% if tailscale_ip_result.rc == 0 %}
Status: Connected
Tailscale IP: {{ tailscale_ip_result.stdout }}
{% else %}
Status: Not activated
Activate with: sudo tailscale up
{% endif %}
────────────────────────────────────────────────────────────
BACKUPS
────────────────────────────────────────────────────────────
Schedule: Daily at 3:00 AM
Location: {{ deployment_dir }}/backups/
Retention: {{ backup_retention_days }} days
────────────────────────────────────────────────────────────
USEFUL COMMANDS
────────────────────────────────────────────────────────────
View containers: cd {{ deployment_dir }} && docker compose ps
View logs: cd {{ deployment_dir }} && docker compose logs -f
Restart service: cd {{ deployment_dir }} && docker compose restart [service]
Manual backup: {{ deployment_dir }}/backup.sh
Nextcloud CLI: docker exec -u www-data next php occ [command]
════════════════════════════════════════════════════════════
- name: Display deployment summary
debug:
msg: |
╔════════════════════════════════════════════════════════════╗
║ DEPLOYMENT COMPLETED SUCCESSFULLY! ║
╚════════════════════════════════════════════════════════════╝
Server: {{ inventory_hostname }} ({{ ansible_host }})
Domain: {{ domain }}
📦 Public Services:
• Nextcloud: https://{{ subdomain_nextcloud }}.{{ domain }}
• OnlyOffice: https://{{ subdomain_office }}.{{ domain }}
• Excalidraw: https://{{ subdomain_draw }}.{{ domain }}
• Obsidian: https://{{ subdomain_notes }}.{{ domain }}
🔒 Management (Tailscale only):
• Homarr: https://{{ subdomain_homarr }}.{{ domain }}
• Dockhand: https://{{ subdomain_dockhand }}.{{ domain }}
• Uptime Kuma: https://{{ subdomain_uptime }}.{{ domain }}
👤 Nextcloud Admin:
Username: {{ admin_user }}
Password: [check vault]
💾 Backups:
Daily at 3:00 AM
Location: {{ deployment_dir }}/backups/
📝 Next Steps:
1. Login to Nextcloud and complete setup
2. Setup Uptime Kuma monitoring (via Tailscale)
3. Configure Homarr dashboard (via Tailscale)
4. Review deployment report: {{ deployment_dir }}/DEPLOYMENT.txt
Deployment report saved to:
{{ deployment_dir }}/DEPLOYMENT.txt
+41
View File
@@ -0,0 +1,41 @@
---
# Playbook 99: Emergency Rollback
# Stop and remove the deployment (keeps backups)
- name: Emergency Rollback
hosts: all
become: yes
tasks:
- name: Confirm rollback
pause:
prompt: |
⚠️ WARNING: This will stop and remove all containers!
Backups in {{ deployment_dir }}/backups/ will be preserved.
Press ENTER to continue or Ctrl+C to cancel
- name: Stop Docker Compose stack
command: docker compose down
args:
chdir: "{{ deployment_dir }}"
become_user: "{{ ansible_user }}"
ignore_errors: yes
- name: Remove deployment directory (except backups)
shell: |
cd {{ deployment_dir }}
rm -rf configs data docker-compose.yml .env
ls -la
args:
warn: false
- name: Display rollback status
debug:
msg: |
✓ Containers stopped and removed
✓ Deployment files removed
✓ Backups preserved in {{ deployment_dir }}/backups/
✓ Docker volumes preserved (use 'docker volume ls' to see)
To completely remove volumes (DELETES ALL DATA):
cd {{ deployment_dir }} && docker compose down -v
+26
View File
@@ -0,0 +1,26 @@
---
# Main Playbook - Nextcloud Stack Deployment
# This orchestrates the complete deployment process
- name: Nextcloud Stack - Complete Deployment
hosts: all
gather_facts: yes
pre_tasks:
- name: Display deployment banner
debug:
msg: |
╔════════════════════════════════════════════════════════════╗
║ Nextcloud Stack Deployment Starting ║
║ Target: {{ inventory_hostname }} ({{ ansible_host }}) ║
║ Domain: {{ domain }} ║
╚════════════════════════════════════════════════════════════╝
- import_playbook: 01-preflight-checks.yml
- import_playbook: 02-system-setup.yml
- import_playbook: 03-docker-setup.yml
- import_playbook: 04-tailscale-setup.yml
- import_playbook: 05-deploy-stack.yml
- import_playbook: 06-configure-caddy.yml
- import_playbook: 07-setup-backups.yml
- import_playbook: 08-post-deployment.yml
+128
View File
@@ -0,0 +1,128 @@
# Caddyfile - Generated by Ansible
# Domain: {{ domain }}
# Global options
{
email {{ user_email }}
}
# ===== PUBLIC SERVICES =====
# Nextcloud
{{ subdomain_nextcloud }}.{{ domain }} {
reverse_proxy next:80
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options nosniff
X-Frame-Options SAMEORIGIN
Referrer-Policy no-referrer
X-XSS-Protection "1; mode=block"
-Server
}
redir /.well-known/carddav /remote.php/dav 301
redir /.well-known/caldav /remote.php/dav 301
redir /.well-known/webfinger /index.php/.well-known/webfinger 301
redir /.well-known/nodeinfo /index.php/.well-known/nodeinfo 301
request_body {
max_size 10GB
}
}
# OnlyOffice Document Server
{{ subdomain_office }}.{{ domain }} {
reverse_proxy onlyoffice:80
request_body {
max_size 100MB
}
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
# Excalidraw
{{ subdomain_draw }}.{{ domain }} {
reverse_proxy excalidraw:80
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
# Obsidian
{{ subdomain_notes }}.{{ domain }} {
reverse_proxy obsidian:3000
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
# ===== TAILSCALE-ONLY SERVICES =====
# Homarr Dashboard
{{ subdomain_homarr }}.{{ domain }} {
@tailscale {
remote_ip 100.64.0.0/10
}
handle @tailscale {
reverse_proxy homarr:7575
}
handle {
respond "Access Denied - Tailscale Required" 403
abort
}
}
# Dockhand Container Manager
{{ subdomain_dockhand }}.{{ domain }} {
@tailscale {
remote_ip 100.64.0.0/10
}
handle @tailscale {
reverse_proxy dockhand:3000
}
handle {
respond "Access Denied - Tailscale Required" 403
abort
}
}
# Uptime Kuma Monitoring
{{ subdomain_uptime }}.{{ domain }} {
@tailscale {
remote_ip 100.64.0.0/10
}
handle @tailscale {
reverse_proxy uptime-kuma:3001
}
handle {
respond "Access Denied - Tailscale Required" 403
abort
}
}
{% if enable_public_status %}
# Public Status Page
status.{{ domain }} {
reverse_proxy uptime-kuma:3001/status
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
{% endif %}
@@ -0,0 +1,229 @@
services:
# ===== DATABASE LAYER =====
next-db:
image: docker.io/postgres:18
container_name: next-db
restart: unless-stopped
environment:
- POSTGRES_DB={{ db_name }}
- POSTGRES_USER={{ db_user }}
- POSTGRES_PASSWORD={{ db_password }}
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- pg_data:/var/lib/postgresql/data
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U {{ db_user }}"]
interval: 10s
timeout: 5s
retries: 5
next-redis:
image: docker.io/redis:7-alpine
container_name: next-redis
restart: unless-stopped
command: redis-server --requirepass {{ redis_password }} --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ===== APPLICATION LAYER =====
next:
image: docker.io/nextcloud:latest
container_name: next
restart: unless-stopped
depends_on:
next-db:
condition: service_healthy
next-redis:
condition: service_healthy
mem_swappiness: -1
environment:
- POSTGRES_DB={{ db_name }}
- POSTGRES_USER={{ db_user }}
- POSTGRES_PASSWORD={{ db_password }}
- POSTGRES_HOST=next-db
- REDIS_HOST=next-redis
- REDIS_HOST_PASSWORD={{ redis_password }}
- NEXTCLOUD_TRUSTED_DOMAINS={{ subdomain_nextcloud }}.{{ domain }}
- NEXTCLOUD_ADMIN_USER={{ admin_user }}
- NEXTCLOUD_ADMIN_PASSWORD={{ admin_password }}
- OVERWRITEPROTOCOL=https
- OVERWRITEHOST={{ subdomain_nextcloud }}.{{ domain }}
- TRUSTED_PROXIES=caddy
- PHP_MEMORY_LIMIT=512M
- PHP_UPLOAD_LIMIT=10G
volumes:
- nextcloud_data:/var/www/html
- {{ deployment_dir }}/configs/nextcloud:/var/www/html/config:Z
- nextcloud_apps:/var/www/html/custom_apps
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/status.php"]
interval: 30s
timeout: 10s
retries: 3
onlyoffice:
image: docker.io/onlyoffice/documentserver:latest
container_name: onlyoffice
restart: unless-stopped
environment:
- JWT_ENABLED=false
- JWT_HEADER=Authorization
- JWT_IN_BODY=true
volumes:
- onlyoffice_data:/var/www/onlyoffice/Data
- onlyoffice_logs:/var/log/onlyoffice
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.monitor-only=true"
excalidraw:
image: docker.io/excalidraw/excalidraw:latest
container_name: excalidraw
restart: unless-stopped
environment:
- NODE_ENV=production
- TZ={{ timezone }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
obsidian:
image: lscr.io/linuxserver/obsidian:latest
container_name: obsidian
restart: unless-stopped
security_opt:
- seccomp:unconfined
environment:
- PUID=1000
- PGID=1000
- TZ={{ timezone }}
volumes:
- {{ deployment_dir }}/data/obsidian/config:/config:z
- {{ deployment_dir }}/data/obsidian/vault:/vault:z
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
shm_size: "1gb"
# ===== INFRASTRUCTURE LAYER =====
caddy:
image: docker.io/caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- {{ deployment_dir }}/configs/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
environment:
- DOMAIN={{ domain }}
- EMAIL={{ user_email }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
homarr:
image: ghcr.io/homarr-labs/homarr:latest
container_name: homarr
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- {{ deployment_dir }}/data/homarr:/appdata
environment:
- SECRET_ENCRYPTION_KEY={{ homarr_secret }}
- TZ={{ timezone }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
dockhand:
image: fnsys/dockhand:latest
container_name: dockhand
restart: unless-stopped
ports:
- "3003:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- dockhand_data:/app/data
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
restart: unless-stopped
volumes:
- uptime_kuma_data:/app/data
environment:
- TZ={{ timezone }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_LABEL_ENABLE=true
- WATCHTOWER_POLL_INTERVAL=86400
- WATCHTOWER_ROLLING_RESTART=true
- WATCHTOWER_INCLUDE_RESTARTING=true
- TZ={{ timezone }}
networks:
- nextcloud_network
# ===== PERSISTENT STORAGE =====
volumes:
pg_data:
redis_data:
nextcloud_data:
nextcloud_apps:
onlyoffice_data:
onlyoffice_logs:
caddy_data:
caddy_config:
dockhand_data:
uptime_kuma_data:
# ===== NETWORKING =====
networks:
nextcloud_network:
name: nextcloud_network
driver: bridge
@@ -0,0 +1,31 @@
# Environment Variables for Nextcloud Stack
# Generated by Ansible - DO NOT EDIT MANUALLY
# Database Configuration
DB_NAME={{ db_name }}
DB_USER={{ db_user }}
DB_PASSWORD={{ db_password }}
DB_HOST=next-db
# Redis Configuration
REDIS_PASSWORD={{ redis_password }}
# Nextcloud Admin
NEXTCLOUD_ADMIN_USER={{ admin_user }}
NEXTCLOUD_ADMIN_PASSWORD={{ admin_password }}
# Application Secrets
HOMARR_SECRET_KEY={{ homarr_secret }}
# Domain Configuration
DOMAIN={{ domain }}
SUBDOMAIN_NEXTCLOUD={{ subdomain_nextcloud }}
SUBDOMAIN_OFFICE={{ subdomain_office }}
# User Configuration
USER_EMAIL={{ user_email }}
TIMEZONE={{ timezone }}
# UIDs/GIDs
PUID=1000
PGID=1000
+463
View File
@@ -0,0 +1,463 @@
#!/bin/bash
#
# Nextcloud Stack - Interactive Setup Script
# This script collects all configuration variables and generates Ansible inventory
#
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Header
clear
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Nextcloud Stack - Ansible Deployment Setup ║${NC}"
echo -e "${CYAN}║ Version 1.0 ║${NC}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "This script will guide you through configuring your Nextcloud"
echo "deployment. Information will be encrypted using Ansible Vault."
echo ""
# Arrays to store server data
declare -a SERVER_IPS
declare -a SERVER_HOSTNAMES
declare -a SERVER_DOMAINS
declare -a SERVER_SSH_USERS
declare -a SERVER_SSH_KEYS
# Global variables
ADMIN_USER=""
ADMIN_PASSWORD=""
USER_NAME=""
USER_EMAIL=""
TIMEZONE="Europe/Zurich"
INSTALL_RCLONE="y"
ENABLE_PUBLIC_STATUS="n"
ALERT_EMAIL=""
TAILSCALE_AUTH_KEY=""
# Subdomain defaults
SUBDOMAIN_NEXTCLOUD="cloud"
SUBDOMAIN_OFFICE="office"
SUBDOMAIN_DRAW="draw"
SUBDOMAIN_NOTES="notes"
SUBDOMAIN_HOMARR="home"
SUBDOMAIN_DOCKHAND="manage"
SUBDOMAIN_UPTIME="uptime"
# Database defaults
DB_NAME="nextcloud"
DB_USER="nextcloud"
# Generate random password
generate_password() {
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
}
# Validate IP address
validate_ip() {
local ip=$1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
return 0
else
return 1
fi
}
# Validate domain
validate_domain() {
local domain=$1
if [[ $domain =~ ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$ ]]; then
return 0
else
return 1
fi
}
# Server Configuration
echo -e "${GREEN}=== Server Configuration ===${NC}"
echo "Enter server details (press Enter without input to finish):"
echo ""
server_count=0
while true; do
((server_count++))
echo -e "${BLUE}Server $server_count:${NC}"
# IP Address
while true; do
read -p " IP address: " ip
if [[ -z "$ip" ]]; then
((server_count--))
break 2
fi
if validate_ip "$ip"; then
SERVER_IPS+=("$ip")
break
else
echo -e "${RED} Invalid IP address. Please try again.${NC}"
fi
done
# Hostname
read -p " Hostname [cloud$(printf "%02d" $server_count)]: " hostname
hostname=${hostname:-cloud$(printf "%02d" $server_count)}
SERVER_HOSTNAMES+=("$hostname")
# Domain
while true; do
read -p " Domain (e.g., example.com): " domain
if [[ -z "$domain" ]]; then
echo -e "${RED} Domain is required. Please try again.${NC}"
elif validate_domain "$domain"; then
SERVER_DOMAINS+=("$domain")
break
else
echo -e "${RED} Invalid domain format. Please try again.${NC}"
fi
done
# SSH User
read -p " SSH user [root]: " ssh_user
ssh_user=${ssh_user:-root}
SERVER_SSH_USERS+=("$ssh_user")
# SSH Key
read -p " SSH key path [~/.ssh/id_rsa]: " ssh_key
ssh_key=${ssh_key:-~/.ssh/id_rsa}
# Expand tilde
ssh_key="${ssh_key/#\~/$HOME}"
SERVER_SSH_KEYS+=("$ssh_key")
echo ""
done
if [[ ${#SERVER_IPS[@]} -eq 0 ]]; then
echo -e "${RED}Error: At least one server is required.${NC}"
exit 1
fi
echo -e "${GREEN}✓ Configured ${#SERVER_IPS[@]} server(s)${NC}"
echo ""
# User Information
echo -e "${GREEN}=== User Information ===${NC}"
read -p "Your name: " USER_NAME
while [[ -z "$USER_NAME" ]]; do
echo -e "${RED}Name is required.${NC}"
read -p "Your name: " USER_NAME
done
read -p "Your email: " USER_EMAIL
while [[ -z "$USER_EMAIL" ]] || [[ ! "$USER_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; do
echo -e "${RED}Valid email is required.${NC}"
read -p "Your email: " USER_EMAIL
done
read -p "Timezone [Europe/Zurich]: " TIMEZONE
TIMEZONE=${TIMEZONE:-Europe/Zurich}
echo ""
# Nextcloud Admin Account
echo -e "${GREEN}=== Nextcloud Admin Account ===${NC}"
read -p "Admin username [admin]: " ADMIN_USER
ADMIN_USER=${ADMIN_USER:-admin}
while true; do
read -sp "Admin password: " ADMIN_PASSWORD
echo ""
read -sp "Confirm password: " ADMIN_PASSWORD_CONFIRM
echo ""
if [[ "$ADMIN_PASSWORD" == "$ADMIN_PASSWORD_CONFIRM" ]] && [[ ${#ADMIN_PASSWORD} -ge 8 ]]; then
break
elif [[ ${#ADMIN_PASSWORD} -lt 8 ]]; then
echo -e "${RED}Password must be at least 8 characters.${NC}"
else
echo -e "${RED}Passwords do not match. Please try again.${NC}"
fi
done
echo ""
# Subdomain Configuration
echo -e "${GREEN}=== Subdomain Configuration ===${NC}"
echo "Press Enter to use defaults shown in brackets"
read -p "Nextcloud subdomain [cloud]: " SUBDOMAIN_NEXTCLOUD
SUBDOMAIN_NEXTCLOUD=${SUBDOMAIN_NEXTCLOUD:-cloud}
read -p "OnlyOffice subdomain [office]: " SUBDOMAIN_OFFICE
SUBDOMAIN_OFFICE=${SUBDOMAIN_OFFICE:-office}
read -p "Excalidraw subdomain [draw]: " SUBDOMAIN_DRAW
SUBDOMAIN_DRAW=${SUBDOMAIN_DRAW:-draw}
read -p "Obsidian subdomain [notes]: " SUBDOMAIN_NOTES
SUBDOMAIN_NOTES=${SUBDOMAIN_NOTES:-notes}
read -p "Homarr subdomain [home]: " SUBDOMAIN_HOMARR
SUBDOMAIN_HOMARR=${SUBDOMAIN_HOMARR:-home}
read -p "Dockhand subdomain [manage]: " SUBDOMAIN_DOCKHAND
SUBDOMAIN_DOCKHAND=${SUBDOMAIN_DOCKHAND:-manage}
read -p "Uptime Kuma subdomain [uptime]: " SUBDOMAIN_UPTIME
SUBDOMAIN_UPTIME=${SUBDOMAIN_UPTIME:-uptime}
echo ""
# Database Configuration
echo -e "${GREEN}=== Database Configuration ===${NC}"
read -p "Database name [nextcloud]: " DB_NAME
DB_NAME=${DB_NAME:-nextcloud}
read -p "Database user [nextcloud]: " DB_USER
DB_USER=${DB_USER:-nextcloud}
echo "Generating secure passwords..."
DB_PASSWORD=$(generate_password)
REDIS_PASSWORD=$(generate_password)
HOMARR_SECRET=$(openssl rand -hex 32)
echo -e "${GREEN}✓ Passwords generated${NC}"
echo ""
# Tailscale Configuration
echo -e "${GREEN}=== Tailscale Configuration ===${NC}"
read -p "Install Tailscale? (y/n) [y]: " install_tailscale
install_tailscale=${install_tailscale:-y}
if [[ "$install_tailscale" == "y" ]]; then
read -p "Tailscale auth key (optional, press Enter to skip): " TAILSCALE_AUTH_KEY
if [[ -z "$TAILSCALE_AUTH_KEY" ]]; then
echo "Note: Tailscale will be installed but not activated."
echo " Activate manually with: sudo tailscale up"
fi
fi
echo ""
# Monitoring Configuration
echo -e "${GREEN}=== Monitoring Configuration ===${NC}"
read -p "Email for uptime alerts: " ALERT_EMAIL
while [[ -z "$ALERT_EMAIL" ]] || [[ ! "$ALERT_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; do
echo -e "${RED}Valid email is required.${NC}"
read -p "Email for uptime alerts: " ALERT_EMAIL
done
read -p "Enable public status page? (y/n) [n]: " ENABLE_PUBLIC_STATUS
ENABLE_PUBLIC_STATUS=${ENABLE_PUBLIC_STATUS:-n}
echo ""
# Backup Configuration
echo -e "${GREEN}=== Backup Configuration ===${NC}"
echo "Backup retention: 30 days (default)"
read -p "Install rclone for future remote backups? (y/n) [y]: " INSTALL_RCLONE
INSTALL_RCLONE=${INSTALL_RCLONE:-y}
if [[ "$INSTALL_RCLONE" == "y" ]]; then
echo "Note: rclone will be installed but not configured."
echo " Configure later with: rclone config"
fi
echo ""
# Ansible Vault Password
echo -e "${GREEN}=== Security ===${NC}"
echo "Create Ansible Vault password (will encrypt all secrets):"
while true; do
read -sp "Vault password: " VAULT_PASSWORD
echo ""
read -sp "Confirm: " VAULT_PASSWORD_CONFIRM
echo ""
if [[ "$VAULT_PASSWORD" == "$VAULT_PASSWORD_CONFIRM" ]] && [[ ${#VAULT_PASSWORD} -ge 8 ]]; then
break
elif [[ ${#VAULT_PASSWORD} -lt 8 ]]; then
echo -e "${RED}Password must be at least 8 characters.${NC}"
else
echo -e "${RED}Passwords do not match. Please try again.${NC}"
fi
done
echo ""
# Configuration Summary
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ CONFIGURATION SUMMARY ║${NC}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Servers: ${#SERVER_IPS[@]}"
for i in "${!SERVER_IPS[@]}"; do
echo "${SERVER_IPS[$i]} (${SERVER_DOMAINS[$i]})"
done
echo ""
echo "Services (per server):"
echo " • Nextcloud: https://$SUBDOMAIN_NEXTCLOUD.<domain>"
echo " • OnlyOffice: https://$SUBDOMAIN_OFFICE.<domain>"
echo " • Excalidraw: https://$SUBDOMAIN_DRAW.<domain>"
echo " • Obsidian: https://$SUBDOMAIN_NOTES.<domain>"
echo " • Homarr: https://$SUBDOMAIN_HOMARR.<domain> (Tailscale)"
echo " • Dockhand: https://$SUBDOMAIN_DOCKHAND.<domain> (Tailscale)"
echo " • Uptime Kuma: https://$SUBDOMAIN_UPTIME.<domain> (Tailscale)"
echo ""
read -p "Proceed with these settings? (y/n): " confirm
if [[ "$confirm" != "y" ]]; then
echo "Setup cancelled."
exit 0
fi
echo ""
# Generate Configuration Files
echo "Generating configuration files..."
# Create inventory directory
mkdir -p inventory/group_vars/all
# Generate hosts.yml
cat >inventory/hosts.yml <<EOF
# Ansible Inventory - Generated by setup.sh
# Date: $(date)
all:
children:
nextcloud_servers:
hosts:
EOF
for i in "${!SERVER_IPS[@]}"; do
cat >>inventory/hosts.yml <<EOF
${SERVER_HOSTNAMES[$i]}:
ansible_host: ${SERVER_IPS[$i]}
ansible_user: ${SERVER_SSH_USERS[$i]}
ansible_ssh_private_key_file: ${SERVER_SSH_KEYS[$i]}
domain: ${SERVER_DOMAINS[$i]}
EOF
done
echo -e "${GREEN}✓ Created: inventory/hosts.yml${NC}"
# Generate vars.yml (public variables)
cat >inventory/group_vars/all/vars.yml <<EOF
# Public Variables - Generated by setup.sh
# Date: $(date)
# User Information
user_name: "$USER_NAME"
user_email: "$USER_EMAIL"
timezone: "$TIMEZONE"
# Admin Account
admin_user: "$ADMIN_USER"
# Subdomain Configuration
subdomain_nextcloud: "$SUBDOMAIN_NEXTCLOUD"
subdomain_office: "$SUBDOMAIN_OFFICE"
subdomain_draw: "$SUBDOMAIN_DRAW"
subdomain_notes: "$SUBDOMAIN_NOTES"
subdomain_homarr: "$SUBDOMAIN_HOMARR"
subdomain_dockhand: "$SUBDOMAIN_DOCKHAND"
subdomain_uptime: "$SUBDOMAIN_UPTIME"
# Database Configuration
db_name: "$DB_NAME"
db_user: "$DB_USER"
# Backup Configuration
backup_retention_days: 30
install_rclone: $INSTALL_RCLONE
# Monitoring
alert_email: "$ALERT_EMAIL"
enable_public_status: $ENABLE_PUBLIC_STATUS
# Deployment Settings
deployment_dir: "/opt/nextcloud-stack"
stack_name: "nextcloud"
# Docker Configuration
docker_compose_version: "v2"
EOF
echo -e "${GREEN}✓ Created: inventory/group_vars/all/vars.yml${NC}"
# Generate vault.yml (encrypted secrets)
VAULT_CONTENT=$(
cat <<EOF
# Encrypted Secrets - Generated by setup.sh
# Date: $(date)
# Admin Password
admin_password: "$ADMIN_PASSWORD"
# Database Credentials
db_password: "$DB_PASSWORD"
redis_password: "$REDIS_PASSWORD"
# Application Secrets
homarr_secret: "$HOMARR_SECRET"
# Tailscale
tailscale_auth_key: "$TAILSCALE_AUTH_KEY"
EOF
)
# Save vault password to temporary file
echo "$VAULT_PASSWORD" >.vault_pass_temp
# Create encrypted vault
echo "$VAULT_CONTENT" | ansible-vault encrypt --vault-password-file=.vault_pass_temp --output=inventory/group_vars/all/vault.yml
# Clean up temp file
rm .vault_pass_temp
echo -e "${GREEN}✓ Created: inventory/group_vars/all/vault.yml (encrypted)${NC}"
echo ""
# Save vault password hint
echo "IMPORTANT: Save your vault password!"
echo "You will need it to run the playbook."
echo ""
echo "Vault password: **************** (hidden)"
echo ""
# Final Instructions
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ READY TO DEPLOY! ║${NC}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Next steps:"
echo ""
echo "1. Review configuration (optional):"
echo " ansible-inventory --list"
echo ""
echo "2. Test connectivity:"
echo " ansible all -m ping --ask-vault-pass"
echo ""
echo "3. Deploy stack:"
echo " ansible-playbook playbooks/site.yml --ask-vault-pass"
echo ""
echo " OR use Makefile:"
echo " make deploy"
echo ""
echo "Estimated deployment time: 30-45 minutes per server"
echo ""
echo -e "${YELLOW}WARNING: Ensure DNS records are configured before deploying!${NC}"
echo " Let's Encrypt will fail if DNS is not pointing correctly."
echo ""
echo "Required DNS records for each server:"
for i in "${!SERVER_DOMAINS[@]}"; do
echo " ${SERVER_DOMAINS[$i]}:"
echo " $SUBDOMAIN_NEXTCLOUD.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_OFFICE.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_DRAW.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_NOTES.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_HOMARR.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_DOCKHAND.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_UPTIME.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo ""
done
echo ""
echo "Setup complete! 🎉"
+1
View File
@@ -0,0 +1 @@
{"binaries":["atuin"],"binary_aliases":{},"cdylibs":[],"cstaticlibs":[],"install_layout":"flat","install_prefix":"/home/liph/.atuin/bin","modify_path":true,"provider":{"source":"cargo-dist","version":"0.31.0"},"source":{"app_name":"atuin","name":"atuin","owner":"atuinsh","release_type":"github"},"version":"18.13.3"}
+368
View File
@@ -0,0 +1,368 @@
## Base directory for Atuin data files (databases, keys, session, etc.)
## All data file paths default to being relative to this directory.
## linux/mac: ~/.local/share/atuin (or XDG_DATA_HOME/atuin)
## windows: %USERPROFILE%/.local/share/atuin
# data_dir = "~/.local/share/atuin"
## where to store your database, default is your system data directory
## linux/mac: ~/.local/share/atuin/history.db
## windows: %USERPROFILE%/.local/share/atuin/history.db
# db_path = "~/.history.db"
## where to store your encryption key, default is your system data directory
## linux/mac: ~/.local/share/atuin/key
## windows: %USERPROFILE%/.local/share/atuin/key
# key_path = "~/.key"
## where to store your auth session token, default is your system data directory
## linux/mac: ~/.local/share/atuin/session
## windows: %USERPROFILE%/.local/share/atuin/session
# session_path = "~/.session"
## date format used, either "us" or "uk"
# dialect = "us"
## default timezone to use when displaying time
## either "l", "local" to use the system's current local timezone, or an offset
## from UTC in the format of "<+|->H[H][:M[M][:S[S]]]"
## for example: "+9", "-05", "+03:30", "-01:23:45", etc.
# timezone = "local"
## enable or disable automatic sync
# auto_sync = true
## enable or disable automatic update checks
# update_check = true
## address of the sync server
# sync_address = "https://api.atuin.sh"
## how often to sync history. note that this is only triggered when a command
## is ran, so sync intervals may well be longer
## set it to 0 to sync after every command
# sync_frequency = "10m"
## which search mode to use
## possible values: prefix, fulltext, fuzzy, skim
# search_mode = "daemon-fuzzy"
## which filter mode to use by default
## possible values: "global", "host", "session", "session-preload", "directory", "workspace"
## consider using search.filters to customize the enablement and order of filter modes
filter_mode = "host"
## With workspace filtering enabled, Atuin will filter for commands executed
## in any directory within a git repository tree (default: false).
##
## To use workspace mode by default when available, set this to true and
## set filter_mode to "workspace" or leave it unspecified and
## set search.filters to include "workspace" before other filter modes.
# workspaces = false
## which filter mode to use when atuin is invoked from a shell up-key binding
## the accepted values are identical to those of "filter_mode"
## leave unspecified to use same mode set in "filter_mode"
filter_mode_shell_up_key_binding = "host"
## which search mode to use when atuin is invoked from a shell up-key binding
## the accepted values are identical to those of "search_mode"
## leave unspecified to use same mode set in "search_mode"
# search_mode_shell_up_key_binding = "fuzzy"
## which style to use
## possible values: auto, full, compact
# style = "auto"
## the maximum number of lines the interface should take up
## set it to 0 to always go full screen
inline_height = 30
## the maximum number of lines the interface should take up
## when atuin is invoked from a shell up-key binding
## the accepted values are identical to those of "inline_height"
# inline_height_shell_up_key_binding = 0
## Invert the UI - put the search bar at the top , Default to `false`
# invert = false
## enable or disable showing a preview of the selected command
## useful when the command is longer than the terminal width and is cut off
show_preview = true
## what to do when the escape key is pressed when searching
## possible values: return-original, return-query
# exit_mode = "return-original"
## possible values: emacs, subl
# word_jump_mode = "emacs"
## characters that count as a part of a word
# word_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
## number of context lines to show when scrolling by pages
## use ctrl instead of alt as the shortcut modifier key for numerical UI shortcuts
## alt-0 .. alt-9
# ctrl_n_shortcuts = false
## Show numeric shortcuts (1..9) beside list items in the TUI
## set to false to hide the moving numbers if you find them distracting
show_numeric_shortcuts = false
## default history list format - can also be specified with the --format arg
# history_format = "{time}\t{command}\t{duration}"
## prevent commands matching any of these regexes from being written to history.
## Note that these regular expressions are unanchored, i.e. if they don't start
## with ^ or end with $, they'll match anywhere in the command.
## For details on the supported regular expression syntax, see
## https://docs.rs/regex/latest/regex/#syntax
# history_filter = [
# "^secret-cmd",
# "^innocuous-cmd .*--secret=.+",
# ]
## prevent commands run with cwd matching any of these regexes from being written
## to history. Note that these regular expressions are unanchored, i.e. if they don't
## start with ^ or end with $, they'll match anywhere in CWD.
## For details on the supported regular expression syntax, see
## https://docs.rs/regex/latest/regex/#syntax
# cwd_filter = [
# "^/very/secret/area",
# ]
## Configure the maximum height of the preview to show.
## Useful when you have long scripts in your history that you want to distinguish
## by more than the first few lines.
# max_preview_height = 4
## Configure whether or not to show the help row, which includes the current Atuin
# show_help = true
## Configure whether or not to show tabs for search and inspect
# show_tabs = true
## Configure whether or not the tabs row may be auto-hidden, which includes the current Atuin
## tab, such as Search or Inspector, and other tabs you may wish to see. This will
## only be hidden if there are fewer than this count of lines available, and does not affect the use
## of keyboard shortcuts to switch tab. 0 to never auto-hide, default is 8 (lines).
## This is ignored except in `compact` mode.
# auto_hide_height = 8
## Defaults to true. This matches history against a set of default regex, and will not save it if we get a match. Defaults include
## 1. AWS key id
## 2. Github pat (old and new)
## 3. Slack oauth tokens (bot, user)
## 4. Slack webhooks
## 5. Stripe live/test keys
# secrets_filter = true
## Defaults to true. If enabled, upon hitting enter Atuin will immediately execute the command,
## whereas tab will put the command in the prompt for editing.
## If set to false, both enter and tab will place the command in the prompt for editing.
## This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
enter_accept = false
search_mode = "fuzzy"
## Defaults to false. If enabled, when triggered after &&, || or |, Atuin will complete commands to chain rather than replace the current line.
# command_chaining = false
## Defaults to "emacs". This specifies the keymap on the startup of `atuin
## search`. If this is set to "auto", the startup keymap mode in the Atuin
## search is automatically selected based on the shell's keymap where the
## keybinding is defined. If this is set to "emacs", "vim-insert", or
## "vim-normal", the startup keymap mode in the Atuin search is forced to be
## the specified one.
# keymap_mode = "auto"
## Cursor style in each keymap mode. If specified, the cursor style is changed
## in entering the cursor shape. Available values are "default" and
## "{blink,steady}-{block,underline,bar}".
# keymap_cursor = { emacs = "blink-block", vim_insert = "blink-block", vim_normal = "steady-block" }
# network_connect_timeout = 5
# network_timeout = 5
## Timeout (in seconds) for acquiring a local database connection (sqlite)
# local_timeout = 5
## Set this to true and Atuin will minimize motion in the UI - timers will not update live, etc.
## Alternatively, set env NO_MOTION=true
# prefers_reduced_motion = false
[stats]
## Set commands where we should consider the subcommand for statistics. Eg, kubectl get vs just kubectl
# common_subcommands = [
# "apt",
# "cargo",
# "composer",
# "dnf",
# "docker",
# "dotnet",
# "git",
# "go",
# "ip",
# "jj",
# "kubectl",
# "nix",
# "nmcli",
# "npm",
# "pecl",
# "pnpm",
# "podman",
# "port",
# "systemctl",
# "tmux",
# "yarn",
# ]
## Set commands that should be totally stripped and ignored from stats
# common_prefix = ["sudo"]
## Set commands that will be completely ignored from stats
# ignored_commands = [
# "cd",
# "ls",
# "vi"
# ]
[keys]
# Defaults to true. If disabled, using the up/down key won't exit the TUI when scrolled past the first/last entry.
# scroll_exits = true
# Defaults to true. The left arrow key will exit the TUI when scrolling before the first character
# exit_past_line_start = true
# Defaults to true. The right arrow key performs the same functionality as Tab and copies the selected line to the command line to be modified.
# accept_past_line_end = true
# Defaults to false. The left arrow key performs the same functionality as Tab and copies the selected line to the command line to be modified.
# accept_past_line_start = false
# Defaults to false. The backspace key performs the same functionality as Tab and copies the selected line to the command line to be modified when at the start of the line.
# accept_with_backspace = false
[sync]
# Enable sync v2 by default
# This ensures that sync v2 is enabled for new installs only
# In a later release it will become the default across the board
records = true
[preview]
## which preview strategy to use to calculate the preview height (respects max_preview_height).
## possible values: auto, static
## auto: length of the selected command.
## static: length of the longest command stored in the history.
## fixed: use max_preview_height as fixed height.
# strategy = "auto"
[daemon]
enabled = true
autostart = true
## Enables using the daemon to sync.
# enabled = false
## Automatically start and manage the daemon when needed.
## Not compatible with `systemd_socket = true`.
# autostart = false
## How often the daemon should sync in seconds
# sync_frequency = 300
## The path to the unix socket used by the daemon (on unix systems)
## linux/mac: ~/.local/share/atuin/atuin.sock
## windows: Not Supported
# socket_path = "~/.local/share/atuin/atuin.sock"
## The daemon pidfile used for lifecycle management.
## Defaults to the Atuin data directory.
# pidfile_path = "~/.local/share/atuin/atuin-daemon.pid"
## Use systemd socket activation rather than opening the given path (the path must still be correct for the client)
## linux: false
## mac/windows: Not Supported
# systemd_socket = false
## The port that should be used for TCP on non unix systems
# tcp_port = 8889
# [theme]
## Color theme to use for rendering in the terminal.
## There are some built-in themes, including the base theme ("default"),
## "autumn" and "marine". You can add your own themes to the "./themes" subdirectory of your
## Atuin config (or ATUIN_THEME_DIR, if provided) as TOML files whose keys should be one or
## more of AlertInfo, AlertWarn, AlertError, Annotation, Base, Guidance, Important, and
## the string values as lowercase entries from this list:
## https://ogeon.github.io/docs/palette/master/palette/named/index.html
## If you provide a custom theme file, it should be called "NAME.toml" and the theme below
## should be the stem, i.e. `theme = "NAME"` for your chosen NAME.
# name = "autumn"
## Whether the theme manager should output normal or extra information to help fix themes.
## Boolean, true or false. If unset, left up to the theme manager.
# debug = true
[search]
## The list of enabled filter modes, in order of priority.
## The "workspace" mode is skipped when not in a workspace or workspaces = false.
## Default filter mode can be overridden with the filter_mode setting.
# filters = [ "global", "host", "session", "session-preload", "workspace", "directory" ]
[tmux]
## Enable using atuin with tmux popup (requires tmux >= 3.2)
## When enabled and running inside tmux, Atuin will use a popup window for interactive search.
## Set to false to disable the popup.
## This can also be controlled with the ATUIN_TMUX_POPUP environment variable.
## Note: The tmux popup is currently supported in zsh, bash, and fish shells. This currently doesn't work with iTerm native tmux integration.
# enabled = false
## Width of the tmux popup window
## Can be a percentage, or integer (e.g. "100" means 100 characters wide)
# width = "80%"
## Height of the tmux popup window
## Can be a percentage, or integer (e.g. "100" means 100 lines tall)
# height = "60%"
[ui]
[ai]
enabled = true
## Columns to display in the interactive search, from left to right.
## The selection indicator (" > ") is always shown first implicitly.
##
## Each column can be specified as a simple string (uses default width)
## or as an object with type, width, and expand:
## { type = "directory", width = 30, expand = true }
##
## Available column types (with default widths):
## duration (5) - Command execution duration (e.g., "123ms")
## time (8) - Relative time since execution (e.g., "59m ago")
## datetime (16) - Absolute timestamp (e.g., "2025-01-22 14:35")
## directory (20) - Working directory (truncated if too long)
## host (15) - Hostname where command was run
## user (10) - Username
## exit (3) - Exit code (colored by success/failure)
## command (*) - The command itself (expands by default)
##
## The "expand" option (default: true for command, false for others) makes a
## column fill remaining space. Only one column should have expand = true.
##
## Default:
# columns = ["duration", "time", "command"]
##
## Examples:
##
## Minimal - more space for commands:
# columns = ["duration", "command"]
##
## With wider directory column:
# columns = ["duration", { type = "directory", width = 30 }, "command"]
##
## Show host for multi-machine sync users:
# columns = ["duration", "time", "host", "command"]
##
## Show exit codes prominently:
# columns = ["exit", "duration", "command"]
##
## Make directory expand instead of command:
# columns = ["duration", "time", { type = "directory", expand = true }, { type = "command", expand = false }]
Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Before

Width:  |  Height:  |  Size: 778 KiB

After

Width:  |  Height:  |  Size: 778 KiB

Before

Width:  |  Height:  |  Size: 974 KiB

After

Width:  |  Height:  |  Size: 974 KiB

Before

Width:  |  Height:  |  Size: 430 KiB

After

Width:  |  Height:  |  Size: 430 KiB

@@ -1,8 +1,8 @@
source = $HOME/.config/hypr/mocha.conf
source = /home/liph/.config/hypr/mocha.conf
$accent = $mauve
$accentAlpha = $mauveAlpha
$font = JetBrainsMono Nerd Font
$font = Inconsolata Nerd Font
# GENERAL
general {
@@ -13,7 +13,7 @@ general {
# BACKGROUND
background {
monitor =
path = $WALLPAPER
path = /home/liph/.config/backgrounds/universe/magenta.jpg
blur_passes = 2
color = $base
}
@@ -2,3 +2,4 @@ wallpaper {
monitor =
path = $WALLPAPER
}
splash = false
+16
View File
@@ -0,0 +1,16 @@
#version 300 es
precision mediump float;
in vec2 v_texcoord;
out vec4 fragColor;
uniform sampler2D tex;
void main() {
vec4 pixColor = texture(tex, v_texcoord);
// Adjust brightness
pixColor.rgb *= 1.00;
fragColor = pixColor;
}
+8 -8
View File
@@ -13,14 +13,14 @@
(defalias
escctrl (tap-hold 250 250 esc lctrl)
a (multi f24 (tap-hold $tap-time $hold-time a lmet))
s (multi f24 (tap-hold $tap-time $hold-time s lalt))
d (multi f24 (tap-hold $tap-time $hold-time d lsft))
f (multi f24 (tap-hold $tap-time $hold-time f lctl))
j (multi f24 (tap-hold $tap-time $hold-time j rctl))
k (multi f24 (tap-hold $tap-time $hold-time k rsft))
l (multi f24 (tap-hold $tap-time $hold-time l ralt))
; (multi f24 (tap-hold $tap-time $hold-time ; rmet))
a (tap-hold $tap-time $hold-time a lmet)
s (tap-hold $tap-time $hold-time s lalt)
d (tap-hold $tap-time $hold-time d lsft)
f (tap-hold $tap-time $hold-time f lctl)
j (tap-hold $tap-time $hold-time j rctl)
k (tap-hold $tap-time $hold-time k rsft)
l (tap-hold $tap-time $hold-time l ralt)
; (tap-hold $tap-time $hold-time ; rmet)
)
(deflayer base
@@ -0,0 +1 @@
/home/liph/.config/systemd/user/gammastep.service
@@ -0,0 +1 @@
/home/liph/.config/systemd/user/kanata.service
@@ -0,0 +1,13 @@
[Unit]
Description=Gamma-colour filter (red-shift) for Wayland
After=graphical-session.target
PartOf=graphical-session.target
[Service]
Type=simple
ExecStart=/usr/bin/gammastep -l 47.38:8.54 -t 6000:4000
Restart=on-failure
RestartSec=3
[Install]
WantedBy=default.target
+83
View File
@@ -0,0 +1,83 @@
[38:2:144:140:170;48:2:42:39:63m [38:2:109:128:134m [22;1;38:2:144:140:170mwob.service
 1 [38:2:156:207:216m┆
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[22;1;38:2:35:33:54;48:2:234:154:151m NORMAL [22;38:2:234:154:151;48:2:42:39:63m[38:2:224:222:244m wob.service [38:2:57:53:82m[38:2:234:154:151;48:2:57:53:82m Top [22;1;38:2:35:33:54;48:2:234:154:151m 1:1
+5 -5
View File
@@ -10,13 +10,13 @@ Environment=DISPLAY=:0
# WARNING: doing so will require the service to run as an elevated user such as root.
# Implementing least privilege access is an exercise left to the reader.
#
CPUSchedulingPolicy=rr
CPUSchedulingPriority=99
IOSchedulingClass=realtime
#CPUSchedulingPolicy=rr
#CPUSchedulingClass=realtim
Nice=-20
Type=simple
ExecStart=/usr/bin/sh -c "exec $$(which kanata) --cfg $$HOME/.config/kanata/config.kbd"
Restart=no
ExecStart=/usr/bin/sh -c "exec $$(which kanata) --cfg $$HOME/.config/kanata/config.kbd --no-wait"
Restart=on failure
RestartkSec=3
[Install]
WantedBy=default.target
+36 -69
View File
@@ -1,80 +1,47 @@
# vim:ft=kitty
background #282828
foreground #d4be98
## name: Catppuccin-Mocha
## author: Pocco81 (https://github.com/Pocco81)
## license: MIT
## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf
## blurb: Soothing pastel theme for the high-spirited!
selection_background #d4be98
selection_foreground #282828
window_padding_width 5 10
cursor #a89984
cursor_text_color background
# The basic colors
foreground #CDD6F4
background #1E1E2E
selection_foreground #1E1E2E
selection_background #F5E0DC
active_tab_background #282828
active_tab_foreground #d4be98
active_tab_font_style bold
inactive_tab_background #282828
inactive_tab_foreground #a89984
inactive_tab_font_style normal
# Cursor colors
cursor #F5E0DC
cursor_text_color #1E1E2E
# Black
color0 #665c54
color8 #928374
# URL underline color when hovering with mouse
url_color #F5E0DC
# Red
color1 #ea6962
color9 #ea6962
# Kitty window border colors
active_border_color #B4BEFE
inactive_border_color #6C7086
bell_border_color #F9E2AF
# Green
color2 #a9b665
color10 #a9b665
# OS Window titlebar colors
wayland_titlebar_color system
macos_titlebar_color system
# Yellow
color3 #e78a4e
color11 #d8a657
# Tab bar colors
active_tab_foreground #11111B
active_tab_background #CBA6F7
inactive_tab_foreground #CDD6F4
inactive_tab_background #181825
tab_bar_background #11111B
# Blue
color4 #7daea3
color12 #7daea3
# Colors for marks (marked text in the terminal)
mark1_foreground #1E1E2E
mark1_background #B4BEFE
mark2_foreground #1E1E2E
mark2_background #CBA6F7
mark3_foreground #1E1E2E
mark3_background #74C7EC
# Magenta
color5 #d3869b
color13 #d3869b
# The 16 terminal colors
# Cyan
color6 #89b482
color14 #89b482
# black
color0 #45475A
color8 #585B70
# red
color1 #F38BA8
color9 #F38BA8
# green
color2 #A6E3A1
color10 #A6E3A1
# yellow
color3 #F9E2AF
color11 #F9E2AF
# blue
color4 #89B4FA
color12 #89B4FA
# magenta
color5 #F5C2E7
color13 #F5C2E7
# cyan
color6 #94E2D5
color14 #94E2D5
# white
color7 #BAC2DE
color15 #A6ADC8
# White
color7 #d4be98
color15 #d4be98
+13 -4
View File
@@ -1,14 +1,23 @@
# BEGIN_KITTY_THEME
# Catppuccin-Mocha
# Gruvbox Material
include current-theme.conf
# END_KITTY_THEME
#
shell zsh --login
font_family Inconsolata Nerd Font
font_family JetBrainsMono Nerd Font
bold_font auto
italic_font auto
bold_italic_font auto
font_size 14
font_size 12
background_opacity 0.90
term xterm-256color
allow_hyperlinks yes
copy_on_select yes
paste_actions confirm-if-large
background_opacity 0.9
+23
View File
@@ -0,0 +1,23 @@
# BEGIN_KITTY_THEME
# Kanagawa
include current-theme.conf
# END_KITTY_THEME
#
shell zsh --login
font_family JetBrainsMono Nerd Font
bold_font auto
italic_font auto
bold_italic_font auto
font_size 12
background_opacity 0.90
term xterm-256color
allow_hyperlinks yes
copy_on_select yes
paste_actions confirm-if-large
+104
View File
@@ -0,0 +1,104 @@
# vim:ft=kitty
## name: Carbonfox
## author: EdenEast
## license: MIT
## upstream: https://github.com/EdenEast/nightfox.nvim/blob/main/extra/carbonfox/kitty.conf
## blurb: Carbonfox theme from the neovim colorscheme nightfox.nvim.
#: All the settings below are colors, which you can choose to modify, or use the
#: defaults. You can also add non-color based settings if needed but note that
#: these will not work with using kitty @ set-colors with this theme. For a
#: reference on what these settings do see https://sw.kovidgoyal.net/kitty/conf/
#: The basic colors
foreground #f2f4f8
background #161616
selection_foreground #f2f4f8
selection_background #2a2a2a
#: Cursor colors
cursor #f2f4f8
cursor_text_color #161616
#: URL underline color when hovering with mouse
url_color #25be6a
#: kitty window border colors and terminal bell colors
active_border_color #78a9ff
inactive_border_color #535353
bell_border_color #3ddbd9
# visual_bell_color none
#: OS Window titlebar colors
# wayland_titlebar_color system
# macos_titlebar_color system
#: Tab bar colors
active_tab_foreground #0c0c0c
active_tab_background #78a9ff
inactive_tab_foreground #6e6f70
inactive_tab_background #2a2a2a
# tab_bar_background none
# tab_bar_margin_color none
#: Colors for marks (marked text in the terminal)
# mark1_foreground black
# mark1_background #98d3cb
# mark2_foreground black
# mark2_background #f2dcd3
# mark3_foreground black
# mark3_background #f274bc
#: The basic 16 colors
#: black
color0 #282828
color8 #484848
#: red
color1 #ee5396
color9 #f16da6
#: green
color2 #25be6a
color10 #46c880
#: yellow
color3 #ebcb8b
color11 #f0d399
#: blue
color4 #78a9ff
color12 #8cb6ff
#: magenta
color5 #be95ff
color13 #c8a5ff
#: cyan
color6 #33b1ff
color14 #52bdff
#: white
color7 #dfdfe0
color15 #e4e4e5
#: You can set the remaining 240 colors as color16 to color255.
color16 #3ddbd9
color17 #ff7eb6
@@ -0,0 +1,80 @@
# vim:ft=kitty
## name: Catppuccin-Mocha
## author: Pocco81 (https://github.com/Pocco81)
## license: MIT
## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf
## blurb: Soothing pastel theme for the high-spirited!
# The basic colors
foreground #CDD6F4
background #1E1E2E
selection_foreground #1E1E2E
selection_background #F5E0DC
# Cursor colors
cursor #F5E0DC
cursor_text_color #1E1E2E
# URL underline color when hovering with mouse
url_color #F5E0DC
# Kitty window border colors
active_border_color #B4BEFE
inactive_border_color #6C7086
bell_border_color #F9E2AF
# OS Window titlebar colors
wayland_titlebar_color system
macos_titlebar_color system
# Tab bar colors
active_tab_foreground #11111B
active_tab_background #CBA6F7
inactive_tab_foreground #CDD6F4
inactive_tab_background #181825
tab_bar_background #11111B
# Colors for marks (marked text in the terminal)
mark1_foreground #1E1E2E
mark1_background #B4BEFE
mark2_foreground #1E1E2E
mark2_background #CBA6F7
mark3_foreground #1E1E2E
mark3_background #74C7EC
# The 16 terminal colors
# black
color0 #45475A
color8 #585B70
# red
color1 #F38BA8
color9 #F38BA8
# green
color2 #A6E3A1
color10 #A6E3A1
# yellow
color3 #F9E2AF
color11 #F9E2AF
# blue
color4 #89B4FA
color12 #89B4FA
# magenta
color5 #F5C2E7
color13 #F5C2E7
# cyan
color6 #94E2D5
color14 #94E2D5
# white
color7 #BAC2DE
color15 #A6ADC8
+66
View File
@@ -0,0 +1,66 @@
# https://draculatheme.com/kitty
#
# Installation instructions:
#
# cp dracula.conf ~/.config/kitty/
# echo "include dracula.conf" >> ~/.config/kitty/kitty.conf
#
# Then reload kitty for the config to take affect.
# Alternatively copy paste below directly into kitty.conf
foreground #f8f8f2
background #282a36
selection_foreground #ffffff
selection_background #44475a
url_color #8be9fd
# black
color0 #21222c
color8 #6272a4
# red
color1 #ff5555
color9 #ff6e6e
# green
color2 #50fa7b
color10 #69ff94
# yellow
color3 #f1fa8c
color11 #ffffa5
# blue
color4 #bd93f9
color12 #d6acff
# magenta
color5 #ff79c6
color13 #ff92df
# cyan
color6 #8be9fd
color14 #a4ffff
# white
color7 #f8f8f2
color15 #ffffff
# Cursor colors
cursor #f8f8f2
cursor_text_color background
# Tab bar colors
active_tab_foreground #282a36
active_tab_background #f8f8f2
inactive_tab_foreground #282a36
inactive_tab_background #6272a4
# Marks
mark1_foreground #282a36
mark1_background #ff5555
# Splits/Windows
active_border_color #f8f8f2
inactive_border_color #6272a4
@@ -0,0 +1,22 @@
# Color theme: Everforest Dark Medium
# Auto-generated by Gogh (https://Gogh-Co.github.io/Gogh/)
color0 #343F44
color1 #E67E80
color2 #A7C080
color3 #DBBC7F
color4 #7FBBB3
color5 #D699B6
color6 #83C092
color7 #D3C6AA
color8 #5C6A72
color9 #F85552
color10 #8DA101
color11 #DFA000
color12 #3A94C5
color13 #DF69BA
color14 #35A77C
color15 #DFDDC8
background #2D353B
foreground #D3C6AA
cursor #D3C6AA
@@ -0,0 +1,47 @@
background #282828
foreground #d4be98
selection_background #d4be98
selection_foreground #282828
cursor #a89984
cursor_text_color background
active_tab_background #282828
active_tab_foreground #d4be98
active_tab_font_style bold
inactive_tab_background #282828
inactive_tab_foreground #a89984
inactive_tab_font_style normal
# Black
color0 #665c54
color8 #928374
# Red
color1 #ea6962
color9 #ea6962
# Green
color2 #a9b665
color10 #a9b665
# Yellow
color3 #e78a4e
color11 #d8a657
# Blue
color4 #7daea3
color12 #7daea3
# Magenta
color5 #d3869b
color13 #d3869b
# Cyan
color6 #89b482
color14 #89b482
# White
color7 #d4be98
color15 #d4be98
+47
View File
@@ -0,0 +1,47 @@
# vim:ft=kitty
## name: Kanagawa
## license: MIT
## author: Tommaso Laurenzi
## upstream: https://github.com/rebelot/kanagawa.nvim/
background #1F1F28
foreground #DCD7BA
selection_background #2D4F67
selection_foreground #C8C093
url_color #72A7BC
cursor #C8C093
# Tabs
active_tab_background #1F1F28
active_tab_foreground #C8C093
inactive_tab_background #1F1F28
inactive_tab_foreground #727169
#tab_bar_background #15161E
# normal
color0 #16161D
color1 #C34043
color2 #76946A
color3 #C0A36E
color4 #7E9CD8
color5 #957FB8
color6 #6A9589
color7 #C8C093
# bright
color8 #727169
color9 #E82424
color10 #98BB6C
color11 #E6C384
color12 #7FB4CA
color13 #938AA9
color14 #7AA89F
color15 #DCD7BA
# extended colors
color16 #FFA066
color17 #FF5D62
+52
View File
@@ -0,0 +1,52 @@
# vim:ft=kitty
## name: Nightfox
## author: EdenEast
## license: MIT
## upstream: https://github.com/EdenEast/nightfox.nvim/blob/main/extra/nightfox/nightfox_kitty.conf
background #192330
foreground #cdcecf
selection_background #2b3b51
selection_foreground #cdcecf
url_color #81b29a
# Cursor
# uncomment for reverse background
# cursor none
cursor #cdcecf
# Border
active_border_color #719cd6
inactive_border_color #39506d
bell_border_color #f4a261
# Tabs
active_tab_background #719cd6
active_tab_foreground #131a24
inactive_tab_background #2b3b51
inactive_tab_foreground #738091
# normal
color0 #393b44
color1 #c94f6d
color2 #81b29a
color3 #dbc074
color4 #719cd6
color5 #9d79d6
color6 #63cdcf
color7 #dfdfe0
# bright
color8 #575860
color9 #d16983
color10 #8ebaa4
color11 #e0c989
color12 #86abdc
color13 #baa1e2
color14 #7ad5d6
color15 #e4e4e5
# extended colors
color16 #f4a261
color17 #d67ad2
+32
View File
@@ -0,0 +1,32 @@
# One Dark by Giuseppe Cesarano, https://github.com/GiuseppeCesarano
# This work is licensed under the terms of the GPL-2.0 license.
# For a copy, see https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html.
# Colors
foreground #979eab
background #282c34
color0 #282c34
color1 #e06c75
color2 #98c379
color3 #e5c07b
color4 #61afef
color5 #be5046
color6 #56b6c2
color7 #979eab
color8 #393e48
color9 #d19a66
color10 #56b6c2
color11 #e5c07b
color12 #61afef
color13 #be5046
color14 #56b6c2
color15 #abb2bf
# Tab Bar
active_tab_foreground #282c34
active_tab_background #979eab
inactive_tab_foreground #abb2bf
inactive_tab_background #282c34
@@ -0,0 +1,56 @@
## name: Rosé Pine Moon
## author: mvllow
## license: MIT
## upstream: https://github.com/rose-pine/kitty/blob/main/dist/rose-pine-moon.conf
## blurb: All natural pine, faux fur and a bit of soho vibes for the classy minimalist
foreground #e0def4
background #232136
selection_foreground #e0def4
selection_background #44415a
cursor #56526e
cursor_text_color #e0def4
url_color #c4a7e7
active_tab_foreground #e0def4
active_tab_background #393552
inactive_tab_foreground #6e6a86
inactive_tab_background #232136
active_border_color #3e8fb0
inactive_border_color #44415a
# black
color0 #393552
color8 #6e6a86
# red
color1 #eb6f92
color9 #eb6f92
# green
color2 #3e8fb0
color10 #3e8fb0
# yellow
color3 #f6c177
color11 #f6c177
# blue
color4 #9ccfd8
color12 #9ccfd8
# magenta
color5 #c4a7e7
color13 #c4a7e7
# cyan
color6 #ea9a97
color14 #ea9a97
# white
color7 #e0def4
color15 #e0def4
+51
View File
@@ -0,0 +1,51 @@
# vim:ft=kitty
## name: Sonokai Sushia
## license: MIT
## author: Dustin Miao
## upstream: https://github.com/selenebun/sonokai-kitty
## blurb: A Kitty port for the Sonokai theme (https://github.com/sainnhe/sonokai)
#: Disclaimer: I am not the original creator of this theme. I have ported it over from
#: https://github.com/selenebun/sonokai-kitty/blob/main/colors/sonokai-shusia.conf.
foreground #e3e1e4
background #2d2a2e
selection_foreground #e3e1e4
selection_background #423f46
cursor #e3e1e4
cursor_text_color background
active_tab_background #2d2a2e
active_tab_foreground #e3e1e4
active_tab_font_style bold
inactive_tab_background #2d2a2e
inactive_tab_foreground #e3e1e4
inactive_tab_font_style normal
color0 #1a181a
color8 #848089
color1 #f85e84
color9 #f85e84
color2 #9ecd6f
color10 #9ecd6f
color3 #e5c463
color11 #e5c463
color4 #7accd7
color12 #7accd7
color5 #ab9df2
color13 #ab9df2
color6 #ef9062
color7 #ef9062
color7 #e3e1e4
color15 #e3e1e4
+614
View File
@@ -0,0 +1,614 @@
// This config is in the KDL format: https://kdl.dev
// "/-" comments out the following node.
// Check the wiki for a full description of the configuration:
// https://yalter.github.io/niri/Configuration:-Introduction
input {
keyboard {
xkb {
layout "ch"
variant "de_mac"
model "apple"
}
}
touchpad {
tap
natural-scroll
}
mouse {
}
trackpoint {
}
}
// x11 support for wayland
xwayland-satellite {
path "xwayland-satellite"
}
environment {
ELECTRON_OZONE_PLATFORM_HINT "auto"
ELECTRON_ENABLE_LOGGING "1"
// GDK_SCALE "1.75"
// GDK_DPI_SCALE "1"
}
output "eDP-1" {
mode "2880x1800@59.990"
scale 1.1
position x=0 y=0
focus-at-startup
}
output "DP-4" {
mode "2560x1440@59.951"
position x=1645 y=0
scale 0.8
}
layout {
gaps 2
center-focused-column "never"
preset-column-widths {
proportion 0.5
proportion 0.66667
proportion 0.3333
}
default-column-width {
proportion 0.5
}
focus-ring {
width 2
active-color "#7daea3"
inactive-color "#928374"
}
border {
off
width 4
active-color "#eba0ac"
inactive-color "#505050"
urgent-color "#9b0000"
}
shadow {
softness 30
spread 5
offset x=0 y=5
color "#0007"
}
struts {
}
}
spawn-at-startup "bash" "/mnt/tank/scripts/fix-bitwarden.sh"
spawn-at-startup "waybar" "-c" "/home/liph/.config/niri/waybar-niri/config.jsonc" "-s" "/home/liph/.config/niri/waybar-niri/style.css"
spawn-at-startup "swaync"
// spawn-at-startup "kanshi"
spawn-at-startup "awww-daemon"
spawn-at-startup "xwayland-satellite"
spawn-at-startup "gammastep" "-l" "47.38:8.54" "-t" "6000:4000"
spawn-at-startup "wezterm"
spawn-at-startup "obsidian"
// spawn-at-startup "kanshi"
spawn-at-startup "librewolf"
spawn-at-startup "sh" "-c" "sleep 5 && exec swayidle -w timeout 300 hyprlock timeout 600 'niri msg action power-off-monitors' resume 'niri msg action power-on-monitors' timeout 3600 'systemctl suspend'"
prefer-no-csd
gestures {
hot-corners {
off
}
}
workspace "1"
workspace "2"
workspace "3"
workspace "4"
// Example: block out two password managers from screen capture.
// (This example rule is commented out with a "/-" in front.)
/-window-rule {
match app-id=r#"^org\.keepassxc\.KeePassXC$"#
match app-id=r#"^org\.gnome\.World\.Secrets$"#
block-out-from "screen-capture"
}
window-rule {
match app-id="Zoom Workplace"
open-floating true
}
// Wezterm window rules
window-rule {
match at-startup=true app-id="wezterm"
open-on-workspace "1"
open-maximized true
open-floating false
// clip-to-geometry true
}
window-rule {
match at-startup=false app-id="wezterm"
open-floating false
// clip-to-geometry true
}
// Obsidian window rules
window-rule {
match at-startup=true app-id="obsidian"
open-on-workspace "3"
open-maximized true
open-floating false
}
window-rule {
match at-startup=false app-id="obsidian"
open-maximized true
open-floating false
}
// Librewolf window rules
window-rule {
match at-startup=true app-id="librewolf"
open-on-workspace "2"
open-maximized true
}
window-rule {
match app-id="librewolf"
open-maximized true
clip-to-geometry true
open-floating false
}
window-rule {
match app-id="Tor Browser"
open-maximized true
clip-to-geometry true
open-floating false
open-on-workspace "2"
}
window-rule {
match app-id="Claude"
open-on-workspace "4"
default-column-width {
proportion 0.6667
}
clip-to-geometry true
}
// 2. Specific rule for windows opening at startup
window-rule {
match app-id="eu.betterbird.Betterbird"
open-maximized true
clip-to-geometry true
open-on-workspace "3"
}
window-rule {
match app-id="Slack"
open-maximized true
clip-to-geometry true
open-on-workspace "4"
}
window-rule {
match app-id="floating_btop"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_htop"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_bluetui"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_pulsemixer"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_wavemon"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_network-tui"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_yazi"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
geometry-corner-radius 5
clip-to-geometry true
}
binds {
// Mod+Shift+Slash { show-hotkey-overlay; }
Mod+Mod5+J {
spawn "hyprlock"
}
Mod+Alt+P hotkey-overlay-title="Open Bitwarden" {
spawn "bitwarden-desktop"
}
Mod+Alt+T hotkey-overlay-title="Open Tipp10" {
spawn "tipp10"
}
Mod+Alt+S hotkey-overlay-title="Open Tidal" {
spawn "tidal-hifi" "--ozone-platform=wayland" "--enable-features=WaylandWindowDecorations"
}
Mod+Alt+O hotkey-overlay-title="Open Obsidian" {
spawn "obsidian"
}
Mod+Alt+C hotkey-overlay-title="Open Claude" {
spawn "claude-desktop"
}
Mod+Alt+Z hotkey-overlay-title="Open Librewolf" {
spawn "librewolf"
}
Mod+Alt+Return hotkey-overlay-title="Open Kitty" {
spawn "wezterm"
}
Mod+Alt+Space hotkey-overlay-title="Open Wofi" {
spawn "wofi" "--show" "drun"
}
Mod+Mod5+Q hotkey-overlay-title="Open Htop" {
spawn "kitty" "--class" "floating_htop" "-e" "htop"
}
Mod+Mod5+W hotkey-overlay-title="Open Btop" {
spawn "kitty" "--class" "floating_btop" "-e" "btop"
}
Mod+Mod5+E hotkey-overlay-title="Open Pulsemixer" {
spawn "kitty" "--class" "floating_pulsemixer" "-e" "pulsemixer"
}
Mod+Mod5+R allow-inhibiting=false hotkey-overlay-title="Open Bluetui" {
spawn "kitty" "--class" "floating_bluetui" "-e" "bluetui"
}
Mod+Mod5+T hotkey-overlay-title="Open Wavemon" {
spawn "kitty" "--class" "floating_wavemon" "-e" "wavemon"
}
Mod+Mod5+Z hotkey-overlay-title="Open network-tui" {
spawn "kitty" "--class" "floating_network-tui" "-e" "network-tui"
}
Mod+Mod5+Y hotkey-overlay-title="Open Yazi" {
spawn "kitty" "--class" "floating_yazi" "-e" "yazi"
}
// Brightness Controll
Mod+Ctrl+0 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 0"
}
Mod+Ctrl+1 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 1"
}
Mod+Ctrl+2 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 2"
}
Mod+Ctrl+3 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 3"
}
// Brightness controls
Mod+Ctrl+4 {
spawn "sh" "-c" "~/scripts/niri_br_up.sh"
}
Mod+Ctrl+5 {
spawn "sh" "-c" "~/scripts/niri_br_down.sh"
}
Mod+Ctrl+6 {
spawn "sh" "-c" "~/scripts/niri_br_blue.sh"
}
Mod+Ctrl+7 {
spawn "sh" "-c" "~/scripts/niri_br_reset.sh"
}
// Swaync Notifications
Mod+Ctrl+8 {
spawn "swaync-client" "-t"
}
Super+Alt+S allow-when-locked=true hotkey-overlay-title=null {
spawn-sh "pkill orca || exec orca"
}
XF86AudioRaiseVolume allow-when-locked=true {
spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+ -l 1.0"
}
XF86AudioLowerVolume allow-when-locked=true {
spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1-"
}
XF86AudioMute allow-when-locked=true {
spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
}
XF86AudioMicMute allow-when-locked=true {
spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
}
XF86AudioPlay allow-when-locked=true {
spawn-sh "playerctl play-pause"
}
XF86AudioStop allow-when-locked=true {
spawn-sh "playerctl stop"
}
XF86AudioPrev allow-when-locked=true {
spawn-sh "playerctl previous"
}
XF86AudioNext allow-when-locked=true {
spawn-sh "playerctl next"
}
XF86MonBrightnessUp allow-when-locked=true {
spawn "brightnessctl" "--class=backlight" "set" "+10%"
}
XF86MonBrightnessDown allow-when-locked=true {
spawn "brightnessctl" "--class=backlight" "set" "10%-"
}
Mod+O repeat=false {
toggle-overview
}
Mod+Shift+C repeat=false {
close-window
}
Mod+Left {
focus-column-left
}
Mod+Down {
focus-window-down
}
Mod+Up {
focus-window-up
}
Mod+Right {
focus-column-right
}
Mod+H {
focus-column-left
}
Mod+J {
focus-window-down
}
Mod+K {
focus-window-up
}
Mod+L {
focus-column-right
}
Mod+Ctrl+Left {
move-column-left
}
Mod+Ctrl+Down {
move-window-down
}
Mod+Ctrl+Up {
move-window-up
}
Mod+Ctrl+Right {
move-column-right
}
Mod+Ctrl+H {
move-column-left
}
Mod+Ctrl+J {
move-window-down
}
Mod+Ctrl+K {
move-window-up
}
Mod+Ctrl+L {
move-column-right
}
// Mod+Shift+Down { focus-monitor-down; }
// Mod+Shift+Up { focus-monitor-up; }
// Mod+Shift+Right { focus-monitor-right; }
Mod+Shift+H {
focus-monitor-left
}
// Mod+Shift+J { focus-monitor-down; }
// Mod+Shift+K { focus-monitor-up; }
Mod+Shift+L {
focus-monitor-right
}
Mod+Shift+Ctrl+Left {
move-column-to-monitor-left
}
// Mod+Shift+Ctrl+Down { move-column-to-monitor-down; }
// Mod+Shift+Ctrl+Up { move-column-to-monitor-up; }
Mod+Shift+Ctrl+Right {
move-column-to-monitor-right
}
Mod+Shift+Ctrl+H {
move-column-to-monitor-left
}
// Mod+Shift+Ctrl+J { move-column-to-monitor-down; }
// Mod+Shift+Ctrl+K { move-column-to-monitor-up; }
Mod+Shift+Ctrl+L {
move-column-to-monitor-right
}
Mod+U {
focus-workspace-down
}
Mod+I {
focus-workspace-up
}
Mod+Alt+J {
move-column-to-workspace-down
}
Mod+Alt+K {
move-column-to-workspace-up
}
Mod+Alt+U {
move-column-to-workspace-down
}
Mod+Alt+I {
move-column-to-workspace-up
}
Mod+Shift+U {
move-workspace-down
}
Mod+Shift+I {
move-workspace-up
}
Mod+WheelScrollDown cooldown-ms=150 {
focus-workspace-down
}
Mod+WheelScrollUp cooldown-ms=150 {
focus-workspace-up
}
Mod+Ctrl+WheelScrollDown cooldown-ms=150 {
move-column-to-workspace-down
}
Mod+Ctrl+WheelScrollUp cooldown-ms=150 {
move-column-to-workspace-up
}
Mod+WheelScrollRight {
focus-column-right
}
Mod+WheelScrollLeft {
focus-column-left
}
Mod+Ctrl+WheelScrollRight {
move-column-right
}
Mod+Ctrl+WheelScrollLeft {
move-column-left
}
Mod+Shift+WheelScrollDown {
focus-column-right
}
Mod+Shift+WheelScrollUp {
focus-column-left
}
Mod+Ctrl+Shift+WheelScrollDown {
move-column-right
}
Mod+Ctrl+Shift+WheelScrollUp {
move-column-left
}
Mod+1 {
focus-workspace 1
}
Mod+2 {
focus-workspace 2
}
Mod+3 {
focus-workspace 3
}
Mod+4 {
focus-workspace 4
}
Mod+5 {
focus-workspace 5
}
Mod+6 {
focus-workspace 6
}
Mod+7 {
focus-workspace 7
}
Mod+8 {
focus-workspace 8
}
Mod+9 {
focus-workspace 9
}
// Mod+Ctrl+1 { move-column-to-workspace 1; }
// Mod+Ctrl+2 { move-column-to-workspace 2; }
// Mod+Ctrl+3 { move-column-to-workspace 3; }
// Mod+Ctrl+4 { move-column-to-workspace 4; }
// Mod+Ctrl+5 { move-column-to-workspace 5; }
// Mod+Ctrl+6 { move-column-to-workspace 6; }
// Mod+Ctrl+7 { move-column-to-workspace 7; }
// Mod+Ctrl+8 { move-column-to-workspace 8; }
// Mod+Ctrl+9 { move-column-to-workspace 9; }
Mod+BracketLeft {
consume-or-expel-window-left
}
Mod+BracketRight {
consume-or-expel-window-right
}
Mod+Comma {
consume-window-into-column
}
Mod+Period {
expel-window-from-column
}
Mod+R {
switch-preset-column-width
}
// Cycling through the presets in reverse order is also possible.
// Mod+R { switch-preset-column-width-back; }
Mod+Shift+R {
switch-preset-window-height
}
Mod+Ctrl+R {
reset-window-height
}
Mod+F {
maximize-column
}
Mod+Shift+F {
fullscreen-window
}
Mod+Ctrl+F {
expand-column-to-available-width
}
// Mod+C { center-column; }
Mod+Ctrl+C {
center-visible-columns
}
Mod+Minus {
set-column-width "-10%"
}
Mod+Equal {
set-column-width "+10%"
}
// Finer height adjustments when in column with other windows.
Mod+Shift+Minus {
set-window-height "-10%"
}
Mod+Shift+Equal {
set-window-height "+10%"
}
// Move the focused window between the floating and the tiling layout.
Mod+V {
toggle-window-floating
}
Mod+Shift+V {
switch-focus-between-floating-and-tiling
}
Mod+W {
toggle-column-tabbed-display
}
// screenshot
Mod+Alt+4 {
spawn "sh" "-c" "grim -g \"$(slurp)\" ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png && notify-send 'Screenshot' 'Area screenshot saved'"
}
// Fullscreen and save
Mod+Alt+5 {
spawn "sh" "-c" "grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png && notify-send -t 2000 'Screenshot' 'Fullscreen screenshot saved'"
}
// Select area to clipboard
Mod+Shift+4 {
spawn "sh" "-c" "grim -g \"$(slurp)\" - | wl-copy && notify-send -t 2000 'Screenshot' 'Area copied to clipboard'"
}
// area screenshot → annotate in swappy
Mod+Shift+5 {
spawn "sh" "-c" "grim -g \"$(slurp)\" - | swappy -f -"
}
Mod+Escape allow-inhibiting=false {
toggle-keyboard-shortcuts-inhibit
}
Mod+Shift+E {
quit
}
Ctrl+Alt+Delete {
quit
}
Mod+Shift+P {
power-off-monitors
}
}
// include "theme.kdl"
+45
View File
@@ -0,0 +1,45 @@
.base {
color: @base;
}
.surface {
color: @surface;
}
.overlay {
color: @overlay;
}
.muted {
color: @muted;
}
.subtle {
color: @subtle;
}
.text {
color: @text;
}
.red {
color: @love;
}
.yellow {
color: @gold;
}
.green {
color: @rose;
}
.rose {
color: @pine;
}
.blue {
color: @foam;
}
.sky {
color: @iris;
}
.highlightLow {
color: @highlightLow;
}
.highlightMed {
color: @highlightMed;
}
.highlightHigh {
color: @highlightHigh;
}
+139
View File
@@ -0,0 +1,139 @@
{
"layer": "top",
"position": "top",
"height": 30,
"spacing": 0,
"modules-left": ["custom/logo", "cpu", "memory", "bluetooth"],
"modules-center": ["niri/workspaces"],
"modules-right": [
"tray",
"power-profiles-daemon",
// "niri/language",
"pulseaudio",
"network",
"battery",
"clock",
"custom/power",
],
"niri/workspaces": {
"format": "{icon}",
"format-icons": {
// Named workspaces
// (you need to configure them in niri)
// "1",
// "2": "2",
// "3": "3",
// "4": "4",
// "5": "5",
// "6": "6",
// Icons by state
"active": "",
"default": "",
},
},
// "mpris": {
// "format": "DEFAULT: {player_icon} {dynamic}",
// "format-paused": "DEFAULT: {status_icon} <i>{dynamic}</i>",
// "player-icons": {
// "default": "▶",
// "mpv": "🎵",
// },
// "status-icons": {
// "paused": "⏸",
// },
// },
"tray": {
"icon-size": 14,
"spacing": 10,
},
"clock": {
"format": "{:%H:%M:%S}",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"format-alt": "{:%Y-%m-%d}",
"interval": 1,
},
"cpu": {
"format": "<span font='14' > </span>{usage}%",
"tooltip": false,
"interval": 1,
"on-click": "kitty --class floating_btop -e btop",
},
"memory": {
"format": "<span font='14'>  </span>{}%",
// "format": "<span font='14' class='red'>  </span> {}%",
"interval": 1,
"on-click": "kitty --class floating_btop -e htop",
},
"battery": {
"interval": 1,
"states": {
"warning": 30,
"critical": 15,
},
"format": "<span font='14' >{icon}</span> {capacity}%",
"format-full": "<span font='14' >{icon}</span> {capacity}%",
"format-charging": "<span font='14' >󰂄</span> {capacity}%",
"format-plugged": "<span font='14' >󰂄</span> {capacity}%",
"format-icons": ["󰁺", "󰁼", "󰁾", "󰂀", "󰂂", "󰁹"],
},
"power-profiles-daemon": {
"format": "<span >{icon}</span>",
"tooltip-format": "Power profile: {profile}\nDriver: {driver}",
"tooltip": true,
"format-icons": {
"default": " ",
"performance": " ",
"balanced": " ",
"power-saver": "󰌪 ",
},
},
"network": {
"format-wifi": "<span font='14' >󰖩</span> ",
"format-ethernet": "<span font='14' >󰈀</span>",
"format-disconnected": "<span font='14' >󱚵</span>",
"format-alt": "{bandwidthDownBytes}",
"interval": 1,
// "on-click": "kitty --class floating_wavemon -e sudo wavemon",
},
"pulseaudio": {
"format": "<span font='14' >{icon}</span> {volume}%",
"format-muted": "<span font='14'>󰝟</span>",
"format-source": "{volume}% ",
"format-source-muted": "",
"format-icons": {
"headphone": "",
// "hands-free": "",
// "headset": "",
// "phone": "",
// "portable": "",
// "car": "",
"default": ["󰕿", "󰖀", "󰕾"],
},
"on-click": "kitty --class floating_pulsemixer -e pulsemixer",
},
"custom/power": {
"format": "⏻",
"tooltip": false,
"on-click": "wlogout -l ~/.config/niri/wlogout/layout",
},
"custom/logo": {
"format": "<span font='14'>󰣇</span>",
"on-click": "wofi --show drun",
},
"bluetooth": {
"format": "<span font='14'> </span>",
"format-disabled": "<span font='14'> </span>",
"format-connected": "<span font='14'> </span>",
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
"tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%",
"on-click": "kitty --class floating_bluetui -e bluetui",
},
// "bluetooth": {
// "format": "<span font='14'>󰂯</span>",
// "format-disabled": "<span font='14'>󰂲</span>",
// "format-connected": "<span font='14'>󰂱</span>",
// "on-click": "kitty --class floating_bluetui -e bluetui",
// },
}
+139
View File
@@ -0,0 +1,139 @@
@import "theme.css";
@import "colors.css";
* {
font-family: "JetBrainsMono";
font-size: 12px;
}
window#waybar {
background-color: @base;
/* background: radial-gradient(alpha(@theme_base_color, 0.4), @theme_base_color); */
color: @theme_fg_color;
transition-property: background-color;
transition-duration: 0.5s;
}
window#waybar.hidden {
/* opacity: 0.2; */
}
button {
border: none;
border-radius: 0;
}
button:hover {
/* background: inherit; */
}
#workspaces {
padding: 5px;
}
#workspaces button {
padding: 0 2px;
background-color: transparent;
color: @theme_fg_color;
border-radius: 10px;
}
#workspaces button:hover {
color: @gold;
}
#workspaces button.active,
#workspaces button.default,
#workspaces button.focused {
background: @rose;
}
#workspaces button.urgent {
background-color: @love;
}
#clock,
#battery,
#cpu,
#memory,
#disk,
#temperature,
#backlight,
#network,
#pulseaudio,
#wireplumber,
#custom-media,
#tray,
#mode,
#idle_inhibitor,
#scratchpad,
#power-profiles-daemon,
#mpd {
padding: 0 10px;
color: @theme_fg_color;
}
#window,
#workspaces {
margin: 0 4px;
}
/* If workspaces is the leftmost module, omit left margin */
.modules-left > widget:first-child > #workspaces {
margin-left: 0;
}
/* If workspaces is the rightmost module, omit right margin */
.modules-right > widget:last-child > #workspaces {
margin-right: 0;
}
#clock {
color: @text;
}
#battery {
color: @iris;
}
#cpu {
color: @iris;
}
#memory {
color: @foam;
}
#backlight,
#network {
color: @gold;
}
#pulseaudio {
color: @rose;
}
#custom-media,
#tray,
#custom-power,
#bluetooth,
#power-profiles-daemon {
padding: 5px 15px;
color: @theme_text_color;
background-color: alpha(@theme_unfocused_bg_color, 0.4);
border-radius: 20px;
margin: 0 4px;
}
#custom-power {
background-color: @love;
border-radius: 20px 0 0 20px;
transition: all 250ms ease-in-out;
}
#battery.charging {
color: #00ff1c;
}
#clock {
border-radius: 20px;
margin: 0 5px;
}
#pulseaudio.muted {
}
#network.disconnected {
}
#tray {
border-radius: 20px;
margin: 0 5px;
}
#custom-logo {
background-color: @pine;
padding: 0 17px;
border-radius: 0 20px 20px 0;
}
#custom-power {
margin: 0;
}
#bluetooth {
border-radius: 20px;
margin: 0 5px;
color: @pine;
}
+15
View File
@@ -0,0 +1,15 @@
@define-color base #191724;
@define-color surface #1f1d2e;
@define-color overlay #26233a;
@define-color muted #6e6a86;
@define-color subtle #908caa;
@define-color text #e0def4;
@define-color love #eb6f92;
@define-color gold #f6c177;
@define-color rose #ea9a97;
@define-color pine #3e8fb0;
@define-color foam #9ccfd8;
@define-color iris #c4a7e7;
@define-color highlightLow #21202e;
@define-color highlightMed #403d52;
@define-color highlightHigh #524f67;
@@ -0,0 +1,26 @@
@define-color rosewater #f5e0dc;
@define-color flamingo #f2cdcd;
@define-color pink #f5c2e7;
@define-color mauve #cba6f7;
@define-color red #f38ba8;
@define-color maroon #eba0ac;
@define-color rose #fab387;
@define-color yellow #f9e2af;
@define-color green #a6e3a1;
@define-color teal #94e2d5;
@define-color sky #89dceb;
@define-color sapphire #74c7ec;
@define-color blue #89b4fa;
@define-color lavender #b4befe;
@define-color text #cdd6f4;
@define-color subtext1 #bac2de;
@define-color subtext0 #a6adc8;
@define-color overlay2 #9399b2;
@define-color overlay1 #7f849c;
@define-color overlay #6c7086;
@define-color highlightedHigh #585b70;
@define-color highlightedMed #45475a;
@define-color highlightedlow #313244;
@define-color base #1e1e2e;
@define-color mantle #181825;
@define-color crust #11111b;
@@ -0,0 +1,16 @@
/*
@define-color base #232136;
@define-color surface #2a273f;
@define-color overlay #393552;
@define-color muted #6e6a86;
@define-color subtle #908caa;
@define-color text #e0def4;
@define-color red #eb6f92;
@define-color yellow #f6c177;
@define-color rose #ea9a97;
@define-color blue #3e8fb0;
@define-color sky #9ccfd8;
@define-color mauve #c4a7e7;
@define-color highlightLow #2a283e;
@define-color highlightMed #44415a;
@define-color highlightHigh #56526e;
@@ -0,0 +1,23 @@
/*
* Variant: Rosé Pine
* Maintainer: DankChoir
*/
@define-color base #191724;
@define-color surface #1f1d2e;
@define-color overlay #26233a;
@define-color muted #6e6a86;
@define-color subtle #908caa;
@define-color text #e0def4;
@define-color love #eb6f92;
@define-color gold #f6c177;
@define-color rose #ebbcba;
@define-color pine #31748f;
@define-color foam #9ccfd8;
@define-color iris #c4a7e7;
@define-color highlightLow #21202e;
@define-color highlightMed #403d52;
@define-color highlightHigh #524f67;
+36
View File
@@ -0,0 +1,36 @@
{
"label": "lock",
"action": "hyprlock",
"text": "Lock",
"keybind": "l"
}
{
"label": "logout",
"action": "niri msg action quit",
"text": "Logout",
"keybind": "e"
}
{
"label": "suspend",
"action": "systemctl suspend",
"text": "Suspend",
"keybind": "u"
}
{
"label": "hibernate",
"action": "systemctl hibernate",
"text": "Hibernate",
"keybind": "h"
}
{
"label": "shutdown",
"action": "systemctl poweroff",
"text": "Shutdown",
"keybind": "s"
}
{
"label": "reboot",
"action": "systemctl reboot",
"text": "Reboot",
"keybind": "r"
}
+10
View File
@@ -0,0 +1,10 @@
alias cl = clear
alias y = yazi
alias neo = neofetch
alias a = aerc
alias oc = opencode
# nvim
alias n = nvim
alias nv = nvim
alias nano = nvim
+1
View File
@@ -0,0 +1 @@
def --env cld [path: path = "~"] { clear; cd $path }
+954
View File
@@ -0,0 +1,954 @@
# Nushell Config File
#
# version = "0.95.0"
# For more information on defining custom themes, see
# https://www.nushell.sh/book/coloring_and_theming.html
# And here is the theme collection
# https://github.com/nushell/nu_scripts/tree/main/themes
let dark_theme = {
# color for nushell primitives
separator: white
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
header: green_bold
empty: blue
# Closures can be used to choose colors for specific values.
# The value (in this case, a bool) is piped into the closure.
# eg) {|| if $in { 'light_cyan' } else { 'light_gray' } }
bool: light_cyan
int: white
filesize: cyan
duration: white
date: purple
range: white
float: white
string: white
nothing: white
binary: white
cell-path: white
row_index: green_bold
record: white
list: white
block: white
hints: dark_gray
search_result: { bg: red fg: white }
shape_and: purple_bold
shape_binary: purple_bold
shape_block: blue_bold
shape_bool: light_cyan
shape_closure: green_bold
shape_custom: green
shape_datetime: cyan_bold
shape_directory: cyan
shape_external: cyan
shape_externalarg: green_bold
shape_external_resolved: light_yellow_bold
shape_filepath: cyan
shape_flag: blue_bold
shape_float: purple_bold
# shapes are used to change the cli syntax highlighting
shape_garbage: { fg: white bg: red attr: b}
shape_glob_interpolation: cyan_bold
shape_globpattern: cyan_bold
shape_int: purple_bold
shape_internalcall: cyan_bold
shape_keyword: cyan_bold
shape_list: cyan_bold
shape_literal: blue
shape_match_pattern: green
shape_matching_brackets: { attr: u }
shape_nothing: light_cyan
shape_operator: yellow
shape_or: purple_bold
shape_pipe: purple_bold
shape_range: yellow_bold
shape_record: cyan_bold
shape_redirection: purple_bold
shape_signature: green_bold
shape_string: green
shape_string_interpolation: cyan_bold
shape_table: blue_bold
shape_variable: purple
shape_vardecl: purple
shape_raw_string: light_purple
}
let light_theme = {
# color for nushell primitives
separator: dark_gray
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
header: green_bold
empty: blue
# Closures can be used to choose colors for specific values.
# The value (in this case, a bool) is piped into the closure.
# eg) {|| if $in { 'dark_cyan' } else { 'dark_gray' } }
bool: dark_cyan
int: dark_gray
filesize: cyan_bold
duration: dark_gray
date: purple
range: dark_gray
float: dark_gray
string: dark_gray
nothing: dark_gray
binary: dark_gray
cell-path: dark_gray
row_index: green_bold
record: dark_gray
list: dark_gray
block: dark_gray
hints: dark_gray
search_result: { fg: white bg: red }
shape_and: purple_bold
shape_binary: purple_bold
shape_block: blue_bold
shape_bool: light_cyan
shape_closure: green_bold
shape_custom: green
shape_datetime: cyan_bold
shape_directory: cyan
shape_external: cyan
shape_externalarg: green_bold
shape_external_resolved: light_purple_bold
shape_filepath: cyan
shape_flag: blue_bold
shape_float: purple_bold
# shapes are used to change the cli syntax highlighting
shape_garbage: { fg: white bg: red attr: b}
shape_globpattern: cyan_bold
shape_int: purple_bold
shape_internalcall: cyan_bold
shape_keyword: cyan_bold
shape_list: cyan_bold
shape_literal: blue
shape_match_pattern: green
shape_matching_brackets: { attr: u }
shape_nothing: light_cyan
shape_operator: yellow
shape_or: purple_bold
shape_pipe: purple_bold
shape_range: yellow_bold
shape_record: cyan_bold
shape_redirection: purple_bold
shape_signature: green_bold
shape_string: green
shape_string_interpolation: cyan_bold
shape_table: blue_bold
shape_variable: purple
shape_vardecl: purple
shape_raw_string: light_purple
}
$env.config = {
show_banner: false
ls: {
use_ls_colors: true
}
rm: {
always_trash: false # always act as if -t was given. Can be overridden with -p
}
table: {
mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other
index_mode: always # "always" show indexes, "never" show indexes, "auto" = show indexes when a table has "index" column
show_empty: true # show 'empty list' and 'empty record' placeholders for command output
padding: { left: 1, right: 1 } # a left right padding of each column in a table
trim: {
methodology: wrapping # wrapping or truncating
wrapping_try_keep_words: true # A strategy used by the 'wrapping' methodology
truncating_suffix: "..." # A suffix used by the 'truncating' methodology
}
header_on_separator: false # show header text on separator/border line
# abbreviated_row_count: 10 # limit data rows from top and bottom after reaching a set point
}
error_style: "fancy" # "fancy" or "plain" for screen reader-friendly error messages
# datetime_format determines what a datetime rendered in the shell would look like.
# Behavior without this configuration point will be to "humanize" the datetime display,
# showing something like "a day ago."
datetime_format: {
# normal: '%a, %d %b %Y %H:%M:%S %z' # shows up in displays of variables or other datetime's outside of tables
# table: '%m/%d/%y %I:%M:%S%p' # generally shows up in tabular outputs such as ls. commenting this out will change it to the default human readable datetime format
}
explore: {
status_bar_background: { fg: "#1D1F21", bg: "#C4C9C6" },
command_bar_text: { fg: "#C4C9C6" },
highlight: { fg: "black", bg: "yellow" },
status: {
error: { fg: "white", bg: "red" },
warn: {}
info: {}
},
selected_cell: { bg: light_blue },
}
history: {
max_size: 100_000 # Session has to be reloaded for this to take effect
sync_on_enter: true # Enable to share history between multiple sessions, else you have to close the session to write history to file
file_format: "plaintext" # "sqlite" or "plaintext"
isolation: false # only available with sqlite file_format. true enables history isolation, false disables it. true will allow the history to be isolated to the current session using up/down arrows. false will allow the history to be shared across all sessions.
}
completions: {
case_sensitive: false # set to true to enable case-sensitive completions
quick: true # set this to false to prevent auto-selecting completions when only one remains
partial: true # set this to false to prevent partial filling of the prompt
algorithm: "prefix" # prefix or fuzzy
external: {
enable: true # set to false to prevent nushell looking into $env.PATH to find more suggestions, `false` recommended for WSL users as this look up may be very slow
max_results: 100 # setting it lower can improve completion performance at the cost of omitting some options
completer: null # check 'carapace_completer' above as an example
}
use_ls_colors: true # set this to true to enable file/path/directory completions using LS_COLORS
}
cursor_shape: {
emacs: block # block, underscore, line, blink_block, blink_underscore, blink_line, inherit to skip setting cursor shape (line is the default)
vi_insert: block # block, underscore, line, blink_block, blink_underscore, blink_line, inherit to skip setting cursor shape (block is the default)
vi_normal: underscore # block, underscore, line, blink_block, blink_underscore, blink_line, inherit to skip setting cursor shape (underscore is the default)
}
color_config: $dark_theme # if you want a more interesting theme, you can replace the empty record with `$dark_theme`, `$light_theme` or another custom record
footer_mode: 25 # always, never, number_of_rows, auto
float_precision: 2 # the precision for displaying floats in tables
buffer_editor: "" # command that will be used to edit the current line buffer with ctrl+o, if unset fallback to $env.EDITOR and $env.VISUAL
use_ansi_coloring: true
bracketed_paste: true # enable bracketed paste, currently useless on windows
edit_mode: vi # emacs, vi
shell_integration: {
# osc2 abbreviates the path if in the home_dir, sets the tab/window title, shows the running command in the tab/window title
osc2: true
# osc7 is a way to communicate the path to the terminal, this is helpful for spawning new tabs in the same directory
osc7: true
# osc8 is also implemented as the deprecated setting ls.show_clickable_links, it shows clickable links in ls output if your terminal supports it. show_clickable_links is deprecated in favor of osc8
osc8: true
# osc9_9 is from ConEmu and is starting to get wider support. It's similar to osc7 in that it communicates the path to the terminal
osc9_9: false
# osc133 is several escapes invented by Final Term which include the supported ones below.
# 133;A - Mark prompt start
# 133;B - Mark prompt end
# 133;C - Mark pre-execution
# 133;D;exit - Mark execution finished with exit code
# This is used to enable terminals to know where the prompt is, the command is, where the command finishes, and where the output of the command is
osc133: true
# osc633 is closely related to osc133 but only exists in visual studio code (vscode) and supports their shell integration features
# 633;A - Mark prompt start
# 633;B - Mark prompt end
# 633;C - Mark pre-execution
# 633;D;exit - Mark execution finished with exit code
# 633;E - NOT IMPLEMENTED - Explicitly set the command line with an optional nonce
# 633;P;Cwd=<path> - Mark the current working directory and communicate it to the terminal
# and also helps with the run recent menu in vscode
osc633: true
# reset_application_mode is escape \x1b[?1l and was added to help ssh work better
reset_application_mode: true
}
render_right_prompt_on_last_line: false # true or false to enable or disable right prompt to be rendered on last line of the prompt.
use_kitty_protocol: false # enables keyboard enhancement protocol implemented by kitty console, only if your terminal support this.
highlight_resolved_externals: false # true enables highlighting of external commands in the repl resolved by which.
recursion_limit: 50 # the maximum number of times nushell allows recursion before stopping it
plugins: {} # Per-plugin configuration. See https://www.nushell.sh/contributor-book/plugins.html#configuration.
plugin_gc: {
# Configuration for plugin garbage collection
default: {
enabled: true # true to enable stopping of inactive plugins
stop_after: 10sec # how long to wait after a plugin is inactive to stop it
}
plugins: {
# alternate configuration for specific plugins, by name, for example:
#
# gstat: {
# enabled: false
# }
}
}
hooks: {
pre_prompt: [{||
if (which direnv | is-empty) {
return
}
try {
direnv export json | from json | default {} | load-env
if 'PATH' in $env {
$env.PATH = ($env.PATH | split row (char esep))
}
} catch {}
}]
pre_execution: [{ null }] # run before the repl input is run
env_change: {
PWD: [] # run if the PWD environment is different since the last repl input
}
display_output: "if (term size).columns >= 100 { table -e } else { table }" # run to display the output of a pipeline
command_not_found: { null } # return an error message when a command is not found
}
menus: [
# Configuration for default nushell menus
# Note the lack of source parameter
{
name: completion_menu
only_buffer_difference: false
marker: "| "
type: {
layout: columnar
columns: 4
col_width: 20 # Optional value. If missing all the screen width is used to calculate column width
col_padding: 2
}
style: {
text: green
selected_text: { attr: r }
description_text: yellow
match_text: { attr: u }
selected_match_text: { attr: ur }
}
}
{
name: ide_completion_menu
only_buffer_difference: false
marker: "| "
type: {
layout: ide
min_completion_width: 0,
max_completion_width: 50,
max_completion_height: 10, # will be limited by the available lines in the terminal
padding: 0,
border: true,
cursor_offset: 0,
description_mode: "prefer_right"
min_description_width: 0
max_description_width: 50
max_description_height: 10
description_offset: 1
# If true, the cursor pos will be corrected, so the suggestions match up with the typed text
#
# C:\> str
# str join
# str trim
# str split
correct_cursor_pos: false
}
style: {
text: green
selected_text: { attr: r }
description_text: yellow
match_text: { attr: u }
selected_match_text: { attr: ur }
}
}
{
name: history_menu
only_buffer_difference: true
marker: "? "
type: {
layout: list
page_size: 10
}
style: {
text: green
selected_text: green_reverse
description_text: yellow
}
}
{
name: help_menu
only_buffer_difference: true
marker: "? "
type: {
layout: description
columns: 4
col_width: 20 # Optional value. If missing all the screen width is used to calculate column width
col_padding: 2
selection_rows: 4
description_rows: 10
}
style: {
text: green
selected_text: green_reverse
description_text: yellow
}
}
]
keybindings: [
{
name: delete_one_word_backward
modifier: alt
keycode: backspace
mode: [emacs, vi_normal, vi_insert]
event: {edit: backspaceword}
}
{
name: completion_menu
modifier: none
keycode: tab
mode: [emacs vi_normal vi_insert]
event: {
until: [
{ send: menu name: completion_menu }
{ send: menunext }
{ edit: complete }
]
}
}
{
name: ide_completion_menu
modifier: control
keycode: char_n
mode: [emacs vi_normal vi_insert]
event: {
until: [
{ send: menu name: ide_completion_menu }
{ send: menunext }
{ edit: complete }
]
}
}
{
name: history_menu
modifier: control
keycode: char_r
mode: [emacs, vi_insert, vi_normal]
event: { send: menu name: history_menu }
}
{
name: help_menu
modifier: none
keycode: f1
mode: [emacs, vi_insert, vi_normal]
event: { send: menu name: help_menu }
}
{
name: completion_previous_menu
modifier: shift
keycode: backtab
mode: [emacs, vi_normal, vi_insert]
event: { send: menuprevious }
}
{
name: next_page_menu
modifier: control
keycode: char_x
mode: emacs
event: { send: menupagenext }
}
{
name: undo_or_previous_page_menu
modifier: control
keycode: char_z
mode: emacs
event: {
until: [
{ send: menupageprevious }
{ edit: undo }
]
}
}
{
name: escape
modifier: none
keycode: escape
mode: [emacs, vi_normal, vi_insert]
event: { send: esc } # NOTE: does not appear to work
}
{
name: cancel_command
modifier: control
keycode: char_c
mode: [emacs, vi_normal, vi_insert]
event: { send: ctrlc }
}
{
name: quit_shell
modifier: control
keycode: char_d
mode: [emacs, vi_normal, vi_insert]
event: { send: ctrld }
}
{
name: clear_screen
modifier: control
keycode: char_l
mode: [emacs, vi_normal, vi_insert]
event: { send: clearscreen }
}
{
name: search_history
modifier: control
keycode: char_q
mode: [emacs, vi_normal, vi_insert]
event: { send: searchhistory }
}
{
name: open_command_editor
modifier: control
keycode: char_o
mode: [emacs, vi_normal, vi_insert]
event: { send: openeditor }
}
{
name: move_up
modifier: none
keycode: up
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: menuup }
{ send: up }
]
}
}
{
name: move_down
modifier: none
keycode: down
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: menudown }
{ send: down }
]
}
}
{
name: move_left
modifier: none
keycode: left
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: menuleft }
{ send: left }
]
}
}
{
name: move_right_or_take_history_hint
modifier: none
keycode: right
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: historyhintcomplete }
{ send: menuright }
{ send: right }
]
}
}
{
name: move_one_word_left
modifier: control
keycode: left
mode: [emacs, vi_normal, vi_insert]
event: { edit: movewordleft }
}
{
name: move_one_word_right_or_take_history_hint
modifier: control
keycode: right
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: historyhintwordcomplete }
{ edit: movewordright }
]
}
}
{
name: move_to_line_start
modifier: none
keycode: home
mode: [emacs, vi_normal, vi_insert]
event: { edit: movetolinestart }
}
{
name: move_to_line_start
modifier: control
keycode: char_a
mode: [emacs, vi_normal, vi_insert]
event: { edit: movetolinestart }
}
{
name: move_to_line_end_or_take_history_hint
modifier: none
keycode: end
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: historyhintcomplete }
{ edit: movetolineend }
]
}
}
{
name: move_to_line_end_or_take_history_hint
modifier: control
keycode: char_e
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: historyhintcomplete }
{ edit: movetolineend }
]
}
}
{
name: move_to_line_start
modifier: control
keycode: home
mode: [emacs, vi_normal, vi_insert]
event: { edit: movetolinestart }
}
{
name: move_to_line_end
modifier: control
keycode: end
mode: [emacs, vi_normal, vi_insert]
event: { edit: movetolineend }
}
{
name: move_up
modifier: control
keycode: char_p
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: menuup }
{ send: up }
]
}
}
{
name: move_down
modifier: control
keycode: char_t
mode: [emacs, vi_normal, vi_insert]
event: {
until: [
{ send: menudown }
{ send: down }
]
}
}
{
name: delete_one_character_backward
modifier: none
keycode: backspace
mode: [emacs, vi_insert]
event: { edit: backspace }
}
{
name: delete_one_word_backward
modifier: control
keycode: backspace
mode: [emacs, vi_insert]
event: { edit: backspaceword }
}
{
name: delete_one_character_forward
modifier: none
keycode: delete
mode: [emacs, vi_insert]
event: { edit: delete }
}
{
name: delete_one_character_forward
modifier: control
keycode: delete
mode: [emacs, vi_insert]
event: { edit: delete }
}
{
name: delete_one_character_backward
modifier: control
keycode: char_h
mode: [emacs, vi_insert]
event: { edit: backspace }
}
{
name: delete_one_word_backward
modifier: control
keycode: char_w
mode: [emacs, vi_insert]
event: { edit: backspaceword }
}
{
name: move_left
modifier: none
keycode: backspace
mode: vi_normal
event: { edit: moveleft }
}
{
name: newline_or_run_command
modifier: none
keycode: enter
mode: emacs
event: { send: enter }
}
{
name: move_left
modifier: control
keycode: char_b
mode: emacs
event: {
until: [
{ send: menuleft }
{ send: left }
]
}
}
{
name: move_right_or_take_history_hint
modifier: control
keycode: char_f
mode: emacs
event: {
until: [
{ send: historyhintcomplete }
{ send: menuright }
{ send: right }
]
}
}
{
name: redo_change
modifier: control
keycode: char_g
mode: emacs
event: { edit: redo }
}
{
name: undo_change
modifier: control
keycode: char_z
mode: emacs
event: { edit: undo }
}
{
name: paste_before
modifier: control
keycode: char_y
mode: emacs
event: { edit: pastecutbufferbefore }
}
{
name: cut_word_left
modifier: control
keycode: char_w
mode: emacs
event: { edit: cutwordleft }
}
{
name: cut_line_to_end
modifier: control
keycode: char_k
mode: emacs
event: { edit: cuttoend }
}
{
name: cut_line_from_start
modifier: control
keycode: char_u
mode: emacs
event: { edit: cutfromstart }
}
{
name: swap_graphemes
modifier: control
keycode: char_t
mode: emacs
event: { edit: swapgraphemes }
}
{
name: move_one_word_left
modifier: alt
keycode: left
mode: emacs
event: { edit: movewordleft }
}
{
name: move_one_word_right_or_take_history_hint
modifier: alt
keycode: right
mode: emacs
event: {
until: [
{ send: historyhintwordcomplete }
{ edit: movewordright }
]
}
}
{
name: move_one_word_left
modifier: alt
keycode: char_b
mode: emacs
event: { edit: movewordleft }
}
{
name: move_one_word_right_or_take_history_hint
modifier: alt
keycode: char_f
mode: emacs
event: {
until: [
{ send: historyhintwordcomplete }
{ edit: movewordright }
]
}
}
{
name: delete_one_word_forward
modifier: alt
keycode: delete
mode: emacs
event: { edit: deleteword }
}
{
name: delete_one_word_backward
modifier: alt
keycode: backspace
mode: emacs
event: { edit: backspaceword }
}
{
name: delete_one_word_backward
modifier: alt
keycode: char_m
mode: emacs
event: { edit: backspaceword }
}
{
name: cut_word_to_right
modifier: alt
keycode: char_d
mode: emacs
event: { edit: cutwordright }
}
{
name: upper_case_word
modifier: alt
keycode: char_u
mode: emacs
event: { edit: uppercaseword }
}
{
name: lower_case_word
modifier: alt
keycode: char_l
mode: emacs
event: { edit: lowercaseword }
}
{
name: capitalize_char
modifier: alt
keycode: char_c
mode: emacs
event: { edit: capitalizechar }
}
# The following bindings with `*system` events require that Nushell has
# been compiled with the `system-clipboard` feature.
# This should be the case for Windows, macOS, and most Linux distributions
# Not available for example on Android (termux)
# If you want to use the system clipboard for visual selection or to
# paste directly, uncomment the respective lines and replace the version
# using the internal clipboard.
{
name: copy_selection
modifier: control_shift
keycode: char_c
mode: emacs
event: { edit: copyselection }
# event: { edit: copyselectionsystem }
}
{
name: cut_selection
modifier: control_shift
keycode: char_x
mode: emacs
event: { edit: cutselection }
# event: { edit: cutselectionsystem }
}
# {
# name: paste_system
# modifier: control_shift
# keycode: char_v
# mode: emacs
# event: { edit: pastesystem }
# }
{
name: select_all
modifier: control_shift
keycode: char_a
mode: emacs
event: { edit: selectall }
}
]
}
def --env cx [arg] {
cd $arg
ls -l
}
alias l = ls --all
alias c = clear
alias ll = ls -l
alias lt = eza --tree --level=2 --long --icons --git
alias v = nvim
alias as = aerospace
alias asr = atuin scripts run
def ff [] {
aerospace list-windows --all | fzf --bind 'enter:execute(bash -c "aerospace focus --window-id {1}")+abort'
}
# Git
alias gc = git commit -m
alias gca = git commit -a -m
alias gp = git push origin HEAD
alias gpu = git pull origin
alias gst = git status
alias glog = git log --graph --topo-order --pretty='%w(100,0,6)%C(yellow)%h%C(bold)%C(black)%d %C(cyan)%ar %C(green)%an%n%C(bold)%C(white)%s %N' --abbrev-commit
alias gdiff = git diff
alias gco = git checkout
alias gb = git branch
alias gba = git branch -a
alias gadd = git add
alias ga = git add -p
alias gcoall = git checkout -- .
alias gr = git remote
alias gre = git reset
# K8s
alias k = kubectl
alias ka = kubectl apply -f
alias kg = kubectl get
alias kd = kubectl describe
alias kdel = kubectl delete
alias kl = kubectl logs
alias kgpo = kubectl get pod
alias kgd = kubectl get deployments
alias kc = kubectx
alias kns = kubens
alias kl = kubectl logs -f
alias ke = kubectl exec -it
# source ~/.zoxide.nu
source ~/.cache/carapace/init.nu
source ~/.local/share/atuin/init.nu
use ~/.cache/starship/init.nu
use ~/.cache/mise/init.nu
$env.DIRENV_LOG_FORMAT = ""
source ~/.config/nushell/vendor/autoload/wt.nu
+3 -22
View File
@@ -1,5 +1,5 @@
# config.nu
#
def --env cld [path: path = "~"] { clear; cd $path }
# Installed by:
# version = "0.103.0"
#
@@ -151,28 +151,9 @@ let light_theme = {
shape_raw_string: light_purple
}
# Example prompt with Nerd Font icons
def create_left_prompt [] {
let dir = ([
(char -u "e0b1"),
($env.PWD | str replace $nu.home-path "~")
] | str join)
let git_branch = (do -i { git rev-parse --abbrev-ref HEAD } | default "")
$"(ansi green)($dir)(ansi reset) (ansi purple)($git_branch)(ansi reset)"
}
$env.PROMPT_COMMAND = { create_left_prompt }
$env.GIT_DISCOVERY_ACROSS_FILESYSTEM = "1" # Allows Git to check parent dirs
def --wrapped git [...args] {
^git ...$args err> /dev/null
}
## Alias
source ~/.alias.nu
source ~/.config/nushell/.alias.nu
source ~/.config/nushell/.calias.nu
### autocompletion
## zoxide
+153
View File
@@ -361,3 +361,156 @@ cld
exit
bat ~/.ssh/id_ed25519.pub
exit
ls ~/.config
exit
y
yazi
cd ~/.config/nushell
ls
nvim config.nu
touch config-om.nu
nvim config-om.nu
exit
y
yazi
ls
q
cd ~/.config/nushell
touch om.env
nvim om.env
exit
cd ~/.config/nushell
nvim config-om.nu
exit
y
yazi
exit
y
yazi
exit
nvim ~/.config/nushell/config-om.nu
nvim ~/.config/nushell/config.nu
cd ~/.config/nushell
touch .alias.nu
nvim ~/.config/nushell/config.nu
nvim .alias.nu
y
yazi
y
yazi
y
exit
y
yazi
nvim ~/.config/nushell/config-om.nu
nvim ~/.config/nushell/.alias.nu
exit
y
gcc
n
nvim ~/.config/nushell/config.nu
exit
y
yazi
n
y
yazi
nvim .alias.nu
y
yazi
nvim ~/.config/nushell/.alias.nu
nv
mv
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/.alias.nu
exit
y
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/.alias.nu
exit
y
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/.alias.nu
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/.alias.nu
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/.alias.nu
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/config.nu
source ~/.config/nushell/config.nu
exit
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/config.nu
source ~/.config/nushell/config.nu
exit
y
n
ls dotfiles/
nvim ~/.config/nushell/.alias.nu
exit
source ~/.config/nushell/config.nu
cld
cd dotfiles/
cld
nvim ~/.config/nushell/.alias.nu
exit
cld
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/.alias.nu
exit
source ~/.config/nushell/config.nu
nvim ~/.config/nushell/.alias.nu
source ~/.config/nushell/config.nu
cd ~/.config/nushell
cld
cd
nvim ~/.config/nushell/.alias.nu
source ~/.config/nushell/config.nu
cd ~/.config/nushell
cld
nvim ~/.config/nushell/.alias.nu
source ~/.config/nushell/config.nu
cd ~/.config/nushell
cld
exit
cd ~/.config/nushell
cd
cd ~/.config/nushell
cld
y
eix
exit
y
source ~/.config/nushell/config.nu
cd ~/.config/nushell
cld
y
source ~/.config/nushell/config.nu
cd ~/.config/nushell
cld
y
exit
cd ~/.config/nushell
cld
cd
cd ~/.config/nushell
y
source ~/.config/nushell/config.nu
cd ~/.config/nushell
cld
y
source ~/.config/nushell/config.nu
cd ~/.config/nushell
cld
y
source ~/.config/nushell/.alias.nu
n ~/.config/nushell/.alias.nu
source ~/.config/nushell/.alias.nu
n
cld
exit
ls dotfiles
cld
ls -la ~/dotfiles/
ls ~/dotfiles/
exit
+116
View File
@@ -0,0 +1,116 @@
#
# Nushell Environment Config File
#
# version = "0.95.0"
def create_left_prompt [] {
let dir = match (do -i { $env.PWD | path relative-to $nu.home-path }) {
null => $env.PWD
'' => '~'
$relative_pwd => ([~ $relative_pwd] | path join)
}
let path_color = (if (is-admin) { ansi red_bold } else { ansi green_bold })
let separator_color = (if (is-admin) { ansi light_red_bold } else { ansi light_green_bold })
let path_segment = $"($path_color)($dir)"
$path_segment | str replace --all (char path_sep) $"($separator_color)(char path_sep)($path_color)"
}
def create_right_prompt [] {
# create a right prompt in magenta with green separators and am/pm underlined
let time_segment = ([
(ansi reset)
(ansi magenta)
(date now | format date '%x %X') # try to respect user's locale
] | str join | str replace --regex --all "([/:])" $"(ansi green)${1}(ansi magenta)" |
str replace --regex --all "([AP]M)" $"(ansi magenta_underline)${1}")
let last_exit_code = if ($env.LAST_EXIT_CODE != 0) {([
(ansi rb)
($env.LAST_EXIT_CODE)
] | str join)
} else { "" }
([$last_exit_code, (char space), $time_segment] | str join)
}
# Use nushell functions to define your right and left prompt
$env.PROMPT_COMMAND = {|| create_left_prompt }
# FIXME: This default is not implemented in rust code as of 2023-09-08.
$env.PROMPT_COMMAND_RIGHT = {|| create_right_prompt }
# The prompt indicators are environmental variables that represent
# the state of the prompt
$env.PROMPT_INDICATOR = {|| "> " }
$env.PROMPT_INDICATOR_VI_INSERT = {|| ": " }
$env.PROMPT_INDICATOR_VI_NORMAL = {|| "> " }
$env.PROMPT_MULTILINE_INDICATOR = {|| "::: " }
# If you want previously entered commands to have a different prompt from the usual one,
# you can uncomment one or more of the following lines.
# This can be useful if you have a 2-line prompt and it's taking up a lot of space
# because every command entered takes up 2 lines instead of 1. You can then uncomment
# the line below so that previously entered commands show with a single `🚀`.
# $env.TRANSIENT_PROMPT_COMMAND = {|| "🚀 " }
# $env.TRANSIENT_PROMPT_INDICATOR = {|| "" }
# $env.TRANSIENT_PROMPT_INDICATOR_VI_INSERT = {|| "" }
# $env.TRANSIENT_PROMPT_INDICATOR_VI_NORMAL = {|| "" }
# $env.TRANSIENT_PROMPT_MULTILINE_INDICATOR = {|| "" }
# $env.TRANSIENT_PROMPT_COMMAND_RIGHT = {|| "" }
# Specifies how environment variables are:
# - converted from a string to a value on Nushell startup (from_string)
# - converted from a value back to a string when running external commands (to_string)
# Note: The conversions happen *after* config.nu is loaded
$env.ENV_CONVERSIONS = {
"PATH": {
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
}
"Path": {
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
}
}
# Directories to search for scripts when calling source or use
# The default for this is $nu.default-config-dir/scripts
$env.NU_LIB_DIRS = [
($nu.default-config-dir | path join 'scripts') # add <nushell-config-dir>/scripts
($nu.data-dir | path join 'completions') # default home for nushell completions
]
# Directories to search for plugin binaries when calling register
# The default for this is $nu.default-config-dir/plugins
$env.NU_PLUGIN_DIRS = [
($nu.default-config-dir | path join 'plugins') # add <nushell-config-dir>/plugins
]
# To add entries to PATH (on Windows you might use Path), you can use the following pattern:
# $env.PATH = ($env.PATH | split row (char esep) | prepend '/some/path')
# An alternate way to add entries to $env.PATH is to use the custom command `path add`
# which is built into the nushell stdlib:
use std "path add"
path add "/opt/homebrew/bin"
path add "/opt/homebrew/sbin"
path add ($env.HOME | path join ".turso")
path add ($env.HOME | path join ".local/share/mise/shims")
path add "/Users/omerxx/.local/bin"
# To load from a custom file you can use:
# source ($nu.default-config-dir | path join 'custom.nu')
mkdir ~/.cache/starship
starship init nu | save -f ~/.cache/starship/init.nu
zoxide init nushell | save -f ~/.zoxide.nu
mkdir ~/.cache/mise
^mise activate nu | save -f ~/.cache/mise/init.nu
$env.STARSHIP_CONFIG = "/Users/omerxx/.config/starship/starship.toml"
$env.CARAPACE_BRIDGES = 'zsh,fish,bash,inshellisense' # optional
mkdir ~/.cache/carapace
carapace _carapace nushell | save --force ~/.cache/carapace/init.nu
$env.EDITOR = "nvim"
+2 -1
View File
@@ -6,6 +6,7 @@
],
"runtime.unicodeName": true,
"diagnostics.disable": [
"undefined-doc-name"
"undefined-doc-name",
"assign-type-mismatch"
]
}
@@ -0,0 +1,7 @@
-- Tell Neovim to treat .ltx as regular tex
vim.filetype.add({
extension = {
ltx = "tex",
tex = "tex",
},
})
+89
View File
@@ -0,0 +1,89 @@
-- after/ftplugin/html.lua (or just :source % once)
local port = 8085
local root = vim.loop.cwd()
local job_id = nil
vim.api.nvim_create_user_command("Serve", function()
-- kill previous python instance
if job_id then
vim.fn.jobstop(job_id)
end
-- one-liner HTTP server + websocket broadcast
local script = ([[
import http.server, os, subprocess, threading, time, mimetypes, json
from http.server import HTTPServer, SimpleHTTPRequestHandler
import websockets, asyncio, pathlib
ROOT = %q
PORT = %d
clients = set()
class Handler(SimpleHTTPRequestHandler):
def log_message(self, fmt, *args): pass # shut up
def do_GET(self):
if self.path == '/':
self.path = '/index.html'
# inject tiny reload script into every HTML
if self.path.endswith('.html'):
ctype = 'text/html; charset=utf-8'
with open(os.path.join(ROOT, self.path.lstrip('/')), 'rb') as f:
content = f.read().decode()
inject = '<script>let ws=new WebSocket("ws://localhost:%d/ws");ws.onmessage=()=>location.reload()</script>'
content = content.replace('</body>', inject .. '</body>')
self.send_response(200)
self.send_header('Content-type', ctype)
self.end_headers()
self.wfile.write(content.encode())
else:
super().do_GET()
async def ws_handler(websocket):
clients.add(websocket)
try:
await websocket.wait_closed()
finally:
clients.remove(websocket)
async def websocket_main():
await websockets.serve(ws_handler, "localhost", %d, path="/ws")
def broadcast_reload():
for c in clients:
asyncio.run(c.send("reload"))
clients.clear()
def reload_loop():
while True:
time.sleep(0.05)
try:
subprocess.check_call(["inotifywait", "-e", "close_write", "-t", "0", ROOT],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
broadcast_reload()
except: pass # inotify not installed → noop (manual F5 still works)
def main():
threading.Thread(target=reload_loop, daemon=True).start()
asyncio.set_event_loop(asyncio.new_event_loop())
threading.Thread(target=lambda: asyncio.run(websocket_main()), daemon=True).start()
HTTPServer(("", PORT), Handler).serve_forever()
if __name__ == '__main__':
main()
]]):format(root, port, port, port)
job_id = vim.fn.jobstart({ "python", "-c", script }, { stdout_buffered = true })
vim.fn.jobstart({ "sleep", "0.2" }, {
on_exit = function()
vim.ui.open(("http://localhost:%d"):format(port))
end,
})
vim.notify("Serving → http://localhost:" .. port, vim.log.levels.INFO)
end, {})
vim.api.nvim_create_user_command("ServeStop", function()
if job_id then
vim.fn.jobstop(job_id)
job_id = nil
end
end, {})
+142
View File
@@ -0,0 +1,142 @@
-- Improved Neovim ftplugin for mail with robust abook integration
-- Debugging enabled: check :messages for logs
local function log(msg)
-- print("[abook] " .. msg)
end
log("ftplugin/mail.lua loaded for filetype: " .. vim.bo.filetype)
-- Omnifunc completion for email addresses
function _G.mail_complete(findstart, base)
if findstart == 1 then
local line = vim.api.nvim_get_current_line()
local col = vim.fn.col(".")
local line_to_cursor = line:sub(1, col - 1)
-- Detect if we are on a header line that takes email addresses
if not line_to_cursor:match("^%s*[Tt][Oo]:") and
not line_to_cursor:match("^%s*[Cc][Cc]:") and
not line_to_cursor:match("^%s*[Bb][Cc][Cc]:") and
not line_to_cursor:match("^%s*[Ff][Rr][Oo][Mm]:") and
not line_to_cursor:match("^%s*[Rr][Ee][Pp][Ll][Yy]%-[Tt][Oo]:") then
return -1
end
-- Find start of current address (after comma or space)
local start = line_to_cursor:reverse():find("[%s,]")
if start then
return col - start
else
return line_to_cursor:find(":") or 0
end
else
-- Query abook
log("Querying abook for: " .. base)
local cmd = string.format('abook --mutt-query "%s" 2>/dev/null', base)
local handle = io.popen(cmd)
if not handle then return {} end
local matches = {}
for line in handle:lines() do
if line ~= "" and not line:match("^%s*$") then
local email, name = line:match("^([^\t]+)\t([^\t]+)")
if email then
local formatted = (name and name ~= "") and string.format("%s <%s>", name, email) or email
table.insert(matches, { word = formatted, abbr = line:gsub("\t", " | "):gsub("%s+$", "") })
end
end
end
handle:close()
log("Found " .. #matches .. " matches")
return matches
end
end
-- Set omnifunc locally
vim.opt_local.omnifunc = "v:lua.mail_complete"
-- Trigger completion on Tab in header lines
vim.keymap.set("i", "<Tab>", function()
local line = vim.api.nvim_get_current_line()
local col = vim.fn.col(".")
local line_to_cursor = line:sub(1, col - 1)
local is_header = line_to_cursor:match("^%s*[Tt][Oo]:") or
line_to_cursor:match("^%s*[Cc][Cc]:") or
line_to_cursor:match("^%s*[Bb][Cc][Cc]:") or
line_to_cursor:match("^%s*[Ff][Rr][Oo][Mm]:") or
line_to_cursor:match("^%s*[Rr][Ee][Pp][Ll][Yy]%-[Tt][Oo]:")
if is_header then
return "<C-x><C-o>"
end
return "<Tab>"
end, { expr = true, buffer = true })
-- Interactive picker
local function pick_email()
log("Manual picker triggered")
local handle = io.popen('abook --mutt-query "" 2>/dev/null')
if not handle then
vim.notify("Could not query abook", vim.log.levels.ERROR)
return
end
local items = {}
for line in handle:lines() do
if line ~= "" and not line:match("^%s*$") then
local email, name = line:match("^([^\t]+)\t([^\t]+)")
if email then
local formatted = (name and name ~= "") and string.format("%s <%s>", name, email) or email
table.insert(items, { display = line:gsub("\t", " | "):gsub("%s+$", ""), value = formatted })
end
end
end
handle:close()
if #items == 0 then
vim.notify("No addresses in abook", vim.log.levels.WARN)
return
end
table.sort(items, function(a, b) return a.display < b.display end)
-- Use Snacks.picker if available, otherwise fallback to vim.ui.select
if _G.Snacks and Snacks.picker then
Snacks.picker.select(items, {
prompt = "Select Recipient",
format_item = function(item) return item.display end,
}, function(choice)
if choice then
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local line = vim.api.nvim_get_current_line()
local before = line:sub(1, col)
local after = line:sub(col + 1)
if before:match("[:%,]$") then choice.value = " " .. choice.value end
vim.api.nvim_set_current_line(before .. choice.value .. after)
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
end
end)
else
vim.ui.select(items, {
prompt = "Select Recipient:",
format_item = function(item) return item.display end,
}, function(choice)
if choice then
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local line = vim.api.nvim_get_current_line()
local before = line:sub(1, col)
local after = line:sub(col + 1)
if before:match("[:%,]$") then choice.value = " " .. choice.value end
vim.api.nvim_set_current_line(before .. choice.value .. after)
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
end
end)
end
end
-- Keybinds
vim.keymap.set("i", "<C-f>", pick_email, { buffer = true, desc = "Pick email from abook" })
vim.keymap.set("n", "<C-f>", pick_email, { buffer = true, desc = "Pick email from abook" })
+40 -9
View File
@@ -1,3 +1,6 @@
-- In your init.lua or after lazy setup
vim.opt.termguicolors = true
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
@@ -11,17 +14,37 @@ if not vim.loop.fs_stat(lazypath) then
end
vim.opt.rtp:prepend(lazypath)
-- Enable spell check for mail files
vim.api.nvim_create_autocmd("FileType", {
pattern = "mail",
callback = function()
vim.opt_local.spell = true
vim.opt_local.spelllang = "en_us,de" -- Add languages you need
end,
})
-- Only enable spell checking for specific filetypes
vim.api.nvim_create_autocmd("FileType", {
pattern = { "markdown", "text", "gitcommit", "tex" },
pattern = { "mail", "markdown", "text", "gitcommit", "tex", "plaintext" },
callback = function()
vim.opt_local.spell = true
vim.opt_local.spelllang = "en_us,de"
end,
})
vim.filetype.add({
extension = {
tex = "tex",
},
})
require("vim-options")
require("lazy").setup("plugins")
-- SINGLE lazy.setup call - combine everything here
require("lazy").setup({
-- Load plugins from the plugins directory
{ import = "plugins" },
})
require("lualine").setup({
sections = {
lualine_x = {
@@ -33,10 +56,18 @@ require("lualine").setup({
},
},
})
require("lazy").setup({
{
"tzachar/cmp-tabnine",
build = "./install.sh",
dependencies = "hrsh7th/nvim-cmp",
},
})
-- In your init.lua or after lazy setup
local theme_file = vim.fn.stdpath("config") .. "/lua/current_theme.lua"
if vim.fn.filereadable(theme_file) == 1 then
dofile(theme_file)
else
vim.cmd.colorscheme("catppuccin") -- fallback
end
vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })
vim.api.nvim_set_hl(0, "NormalNC", { bg = "none" })
vim.api.nvim_set_hl(0, "SignColumn", { bg = "none" })
vim.api.nvim_set_hl(0, "FloatBorder", { bg = "none" })
vim.api.nvim_set_hl(0, "LineNr", { bg = "none" })
+51 -35
View File
@@ -1,60 +1,76 @@
{
"Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" },
"LuaSnip": { "branch": "master", "commit": "fb525166ccc30296fb3457441eb979113de46b00" },
"LuaSnip": { "branch": "master", "commit": "642b0c595e11608b4c18219e93b88d7637af27bc" },
"R.nvim": { "branch": "main", "commit": "fc42c220764268cfa28a2a17a776bb5f4eb94f46" },
"barbecue": { "branch": "main", "commit": "cd7e7da622d68136e13721865b4d919efd6325ed" },
"catppuccin": { "branch": "main", "commit": "ce8d176faa4643e026e597ae3c31db59b63cef09" },
"catppuccin": { "branch": "main", "commit": "426dbebe06b5c69fd846ceb17b42e12f890aedf1" },
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
"cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" },
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
"cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" },
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
"cmp-tabnine": { "branch": "main", "commit": "c0167cdc86c15e782c5461ee62aebee89231c2ed" },
"cmp-r": { "branch": "main", "commit": "70bfe8f4c062acc10266e24825439c009a0b1b89" },
"cmp-vimtex": { "branch": "master", "commit": "c09ca05bfa0641754516f13294d73d3799a02fc8" },
"cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
"conform.nvim": { "branch": "master", "commit": "5f5152fae39a57e3a04cbce09fb21e10c49c9d95" },
"dashboard-nvim": { "branch": "master", "commit": "0775e567b6c0be96d01a61795f7b64c1758262f6" },
"conform.nvim": { "branch": "master", "commit": "dca1a190aa85f9065979ef35802fb77131911106" },
"dashboard-nvim": { "branch": "master", "commit": "f787e3462c2ee2b6117b17c1aa4ddf66cb6f57fe" },
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
"dracula.nvim": { "branch": "main", "commit": "ae752c13e95fb7c5f58da4b5123cb804ea7568ee" },
"dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
"everforest": { "branch": "master", "commit": "aeef62ee97872d2557d25904d160ec93a4a355f8" },
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
"friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
"fzf-lua": { "branch": "main", "commit": "9d579feab4d3035627150e5e9b6e8fbf5e814ef6" },
"gitsigns.nvim": { "branch": "main", "commit": "5813e4878748805f1518cee7abb50fd7205a3a48" },
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
"fzf-lua": { "branch": "main", "commit": "17e3507b788699776ba6d2f8dd101ec177f37a96" },
"gitsigns.nvim": { "branch": "main", "commit": "dd3f588bacbeb041be6facf1742e42097f62165d" },
"grapple.nvim": { "branch": "main", "commit": "b41ddfc1c39f87f3d1799b99c2f0f1daa524c5f7" },
"harpoon": { "branch": "harpoon2", "commit": "87b1a3506211538f460786c23f98ec63ad9af4e5" },
"indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
"gruvbox-mat": { "branch": "master", "commit": "11d779b26a9ab2b3db8c22c6ac9fb6e8ed4fea79" },
"indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" },
"kanagawa.nvim": { "branch": "master", "commit": "8ad3b4cdcc804b332c32db8f9743667e1bb82b99" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
"lazydev.nvim": { "branch": "main", "commit": "5231c62aa83c2f8dc8e7ba957aa77098cda1257d" },
"lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" },
"luvit-meta": { "branch": "main", "commit": "0ea4ff636c5bb559ffa78108561d0976f4de9682" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "8f1a3e6eecb638817e8999aaa16ada27cd54d867" },
"mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" },
"mason.nvim": { "branch": "main", "commit": "57e5a8addb8c71fb063ee4acda466c7cf6ad2800" },
"neo-tree.nvim": { "branch": "v3.x", "commit": "f3df514fff2bdd4318127c40470984137f87b62e" },
"neogit": { "branch": "master", "commit": "d8bf9102692250193b855acd9025a826f1af2729" },
"lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" },
"live-preview.nvim": { "branch": "main", "commit": "c1fcf75c5f9c9c01dd392852de44204b60f1b5b1" },
"lualine.nvim": { "branch": "master", "commit": "131a558e13f9f28b15cd235557150ccb23f89286" },
"luvit-meta": { "branch": "main", "commit": "cc9b2d412d2fbd30b94a70cfc214c2a3be27a0a2" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "0c2823e0418f3d9230ff8b201c976e84de1cb401" },
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
"mason.nvim": { "branch": "main", "commit": "cb8445f8ce85d957416c106b780efd51c6298f89" },
"neo-tree.nvim": { "branch": "v3.x", "commit": "84c75e7a7e443586f60508d12fc50f90d9aee14e" },
"neogit": { "branch": "master", "commit": "e74dfb42c04b493031f323aec8fa5f28b0427b9e" },
"nightfox.nvim": { "branch": "main", "commit": "26b61b1f856ec37cae3cb64f5690adb955f246a1" },
"noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
"nvim-autopairs": { "branch": "master", "commit": "c2a0dd0d931d0fb07665e1fedb1ea688da3b80b4" },
"nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" },
"nvim-lint": { "branch": "master", "commit": "7a64f4067065c16a355d40d0d599b8ca6b25de6d" },
"nvim-lspconfig": { "branch": "master", "commit": "d20d83b3f24f5884da73a9fc92fdc47e778b8d0d" },
"nvim-navic": { "branch": "master", "commit": "7d914a39a1ef8f4e22c2c4381abeef7c556f5a13" },
"nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
"nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
"nvim-colorizer.lua": { "branch": "master", "commit": "a065833f35a3a7cc3ef137ac88b5381da2ba302e" },
"nvim-lint": { "branch": "master", "commit": "eab58b48eb11d7745c11c505e0f3057165902461" },
"nvim-lspconfig": { "branch": "master", "commit": "31026a13eefb20681124706a79fc1df6bf11ab27" },
"nvim-navic": { "branch": "master", "commit": "f5eba192f39b453675d115351808bd51276d9de5" },
"nvim-notify": { "branch": "master", "commit": "8701bece920b38ea289b457f902e2ad184131a5d" },
"nvim-spectre": { "branch": "master", "commit": "72f56f7585903cd7bf92c665351aa585e150af0f" },
"nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" },
"nvim-surround": { "branch": "main", "commit": "2e93e154de9ff326def6480a4358bfc149d5da2c" },
"nvim-tmux-navigation": { "branch": "main", "commit": "4898c98702954439233fdaf764c39636681e2861" },
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
"nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" },
"nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" },
"nvim-web-devicons": { "branch": "master", "commit": "6788013bb9cb784e606ada44206b0e755e4323d7" },
"nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
"nvim-treesitter-textobjects": { "branch": "main", "commit": "851e865342e5a4cb1ae23d31caf6e991e1c99f1e" },
"nvim-ts-context-commentstring": { "branch": "main", "commit": "6141a40173c6efa98242dc951ed4b6f892c97027" },
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
"nvim-web-devicons": { "branch": "master", "commit": "4fc505ac7bd7692824a142e96e5f529c133862f8" },
"obsidian.nvim": { "branch": "main", "commit": "ae1f76a75c7ce36866e1d9342a8f6f5b9c2caf9b" },
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
"onedark-warm": { "branch": "master", "commit": "df4792accde9db0043121f32628bcf8e645d9aea" },
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
"portal.nvim": { "branch": "main", "commit": "77d9d53fec945bfa407d5fd7120f1b4f117450ed" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "8aafe2cbd89cd4090f573a98cab6b20366576fde" },
"render-markdown.nvim": { "branch": "main", "commit": "07d088bf8bdadd159eb807b90eaee86a4778383f" },
"promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "08783ec022e7ddefe0f12a16f1ac4968f55478b0" },
"render-markdown.nvim": { "branch": "main", "commit": "3f3eea97b80839f629c951ca660ffd125bfa5b34" },
"rose-pine": { "branch": "main", "commit": "6a961effd67f6130d36df6d1c05c48c739796dd2" },
"snacks.nvim": { "branch": "main", "commit": "ad9ede6a9cddf16cedbd31b8932d6dcdee9b716e" },
"sonokai": { "branch": "master", "commit": "b023c5280b16fe2366f5e779d8d2756b3e5ee9c3" },
"telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" },
"telescope.nvim": { "branch": "master", "commit": "e709d31454ee6e6157f0537f861f797bd44c0bad" },
"telescope.nvim": { "branch": "master", "commit": "f04ab730b8f9c6bf3f54a206d0dcddfd70c52d59" },
"todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" },
"toggleterm.nvim": { "branch": "main", "commit": "50ea089fc548917cc3cc16b46a8211833b9e3c7c" },
"tokyonight": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" },
"trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" },
"vim-test": { "branch": "master", "commit": "aa619692ff48a3cf3e6bdb893765039488d4e5f3" },
"vimux": { "branch": "master", "commit": "614f0bb1fb598f97accdcea71d5f7b18d7d62436" },
"vimtex": { "branch": "master", "commit": "97e11bd4f56d46a87f8593d6ccb27820e19c4ab0" },
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" },
"yazi.nvim": { "branch": "main", "commit": "d87cd885e06d50e1bb5607e48d96c1094d3b10e3" }
"yazi.nvim": { "branch": "main", "commit": "aada50881aca7d13422e02a6681b8f0a0ec9a75c" }
}
+10
View File
@@ -0,0 +1,10 @@
-- vim.cmd.colorscheme("rose-pine")
-- vim.cmd.colorscheme("sonokai")
-- vim.cmd.colorscheme("one-dark")
-- vim.cmd.colorscheme("nightfox")
-- vim.cmd.colorscheme("kanagawa")
vim.cmd.colorscheme("gruvbox-material")
-- vim.cmd.colorscheme("everforest")
-- vim.cmd.colorscheme("dracula")
-- vim.cmd.colorscheme("carbonfox")
-- vim.cmd.colorscheme("tokyonight")
@@ -0,0 +1,45 @@
-- ~/.config/nvim/lua/plugins/blink.lua
return {
"saghen/blink.cmp",
version = "1.*",
build = "cargo build --release",
lazy = false,
dependencies = {
"L3MON4D3/LuaSnip",
"rafamadriz/friendly-snippets",
},
opts = {
fuzzy = { implementation = "prefer_rust" },
sources = {
min_keyword_length = 2,
default = { "lsp", "snippets", "buffer", "path", "lua" },
per_filetype = {
lua = { default = { "lazydev", "lsp", "snippets", "buffer", "path" } },
},
},
completion = {
accept = { auto_brackets = { enabled = true } },
trigger = { show_on_trigger_character = true },
list = {
selection = {
preselect = function(ctx) return ctx.mode ~= "cmdline" end,
},
},
documentation = {
auto_show = true,
auto_show_delay_ms = 100,
},
},
signature = { enabled = true, window = { border = "rounded" } },
cmdline = { enabled = false },
},
config = function(_, opts)
require("blink.cmp").setup(opts)
end,
}
@@ -0,0 +1,60 @@
-- ~/.config/nvim/lua/plugins/blink.lua
return {
"saghen/blink.cmp",
version = "1.*",
build = "cargo build --release",
lazy = false,
dependencies = {
"L3MON4D3/LuaSnip",
"rafamadriz/friendly-snippets",
},
opts = {
fuzzy = { implementation = "prefer_rust" },
sources = {
min_keyword_length = 1,
default = { "lsp", "snippets", "buffer", "path" },
providers = {
lsp = { name = "LSP", opts = { max_items = 200, score_offset = 0 } },
buffer = { name = "Buffer", opts = { max_items = 100 } },
},
per_filetype = {
lua = { default = { "lazydev", "lsp", "snippets", "buffer", "path" } },
},
},
completion = {
accept = { auto_brackets = { enabled = true } },
trigger = { show_on_trigger_character = true },
list = { selection = "auto_insert" },
documentation = { auto_show = true, auto_show_delay_ms = 200 },
ghost_text = { enabled = true },
},
signature = { enabled = true, window = { border = "rounded" } },
cmdline = { enabled = false },
-- Tab cycles through items and accepts
keymap = {
preset = "enter",
["<Tab>"] = {
"select_next",
"accept",
"snippet_forward",
"fallback",
},
["<S-Tab>"] = {
"select_prev",
"snippet_backward",
"fallback",
},
["<CR>"] = { "accept", "fallback" },
},
},
config = function(_, opts)
require("blink.cmp").setup(opts)
end,
}
@@ -1,9 +0,0 @@
return {
{
"hrsh7th/cmp-path", -- file and folders
"hrsh7th/cmp-buffer", -- words from the current buffer
"hrsh7th/cmp-nvim-lsp", -- LSP-based autocompletions
"hrsh7th/cmp-cmdline", -- Command-line autocompletions
"tzachar/cmp-tabnine"--, build = "./install.sh",
},
}

Some files were not shown because too many files have changed in this diff Show More