diff --git a/bin/Makefile b/bin/Makefile
index 9026d2f..364366c 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -14,7 +14,8 @@
PREFIX= $(HOME)
BIN= ZZZ browser credentials fetch imgresize invidious rfcopen screenshot \
- tor-browser w3m-copy-link xsekrit
+ sekrit tor-browser w3m-copy-link xsekrit
+MAN1= sekrit.1
all: $(BIN)
@@ -25,6 +26,9 @@ install: all
mkdir -p $(PREFIX)/bin
cp -f $(BIN) $(PREFIX)/bin
cd $(PREFIX)/bin && chmod 555 $(BIN)
+ mkdir -p $(PREFIX)/share/man/man1
+ cp -f $(MAN1) $(PREFIX)/share/man/man1
+ cd $(PREFIX)/share/man/man1 && chmod 444 $(MAN1)
uninstall:
cd $(PREFIX)/bin && rm -f $(BIN)
diff --git a/bin/sekrit.1 b/bin/sekrit.1
new file mode 100644
index 0000000..1632bf9
--- /dev/null
+++ b/bin/sekrit.1
@@ -0,0 +1,138 @@
+.\"
+.\" sekrit.1
+.\" Written in 2018 by Lucas
+.\" CC0 1.0 Universal/Public domain - No rights reserved
+.\"
+.\" To the extent possible under law, the author(s) have dedicated all
+.\" copyright and related and neighboring rights to this software to the
+.\" public domain worldwide. This software is distributed without any
+.\" warranty. You should have received a copy of the CC0 Public Domain
+.\" Dedication along with this software. If not, see
+.\" .
+.\"
+.Dd September 25, 2018
+.Dt SEKRIT 1
+.Os
+.Sh NAME
+.Nm sekrit
+.Nd Secret files manager
+.Sh SYNOPSIS
+.Nm sekrit
+.Cm add
+.Ar key
+.Op Ar value ...
+.Nm sekrit
+.Cm gen
+.Op Fl l Ar length
+.Op Ar chars
+.Nm sekrit
+.Cm get
+.Ar key
+.Nm sekrit
+.Cm has
+.Ar key
+.Nm sekrit
+.Cm ls
+.Op Ar keys ...
+.Sh DESCRIPTION
+.Nm
+is a small shell script for managing encrypted files.
+It leverages
+.Xr gpg2 1
+to create and read encrypted files,
+and can generate random data to populate them.
+.Pp
+Because of this,
+.Nm
+can be used as an account credentials manager,
+or as a general-purpose key-value store of encrypted information.
+.Bl -tag -width Ds
+.It Nm Cm add Ar key Op Ar value ...
+Adds a value to
+.Ar key .
+.Ar value
+is inserted as is, without any extra modifications.
+If no
+.Ar value
+is specified on command line,
+.Cm add
+will read the value from standard input.
+.Cm add
+will fail if
+.Ar key
+already has a value.
+.It Nm Cm gen Oo Fl l Ar length Oc Op Ar chars
+Outputs a randomly generated sequence.
+The generated sequence consist of characters
+.Ar chars .
+Defaults to
+.Ar +/0-9A-Za-z .
+If
+.Fl l Ar length
+is provided, the randomly generated sequence will be
+.Ar length
+characters long.
+Defaults to 43.
+.It Nm Cm get Ar key
+Decrypts the value associated with
+.Ar key
+and prints it to standard output.
+Fails if
+.Ar key
+doesn't have a value associated with it.
+.It Nm Cm has Ar key
+Returns success if
+.Ar key
+has a value associated with it.
+Fails otherwise.
+.It Nm Cm ls Op Ar keys ...
+For each
+.Ar key
+given as argument,
+list all the registered keys under that hierarchy.
+If no
+.Ar key
+is given, list all the registeres keys.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width SEKRIT_GPG_ID
+.It Ev SEKRIT_DIR
+Secret files base directory.
+Defaults to
+.Pa ~/keep/sekrit .
+.It Ev SEKRIT_GPG_ID
+The recipient to whom encrypt the files.
+Defaults to
+.Ar myself .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+To use
+.Nm
+as an account credentials manager, you can run
+.Bd -literal -offset indent
+sekrit add accounts/example.com/user myuser
+sekrit gen accounts/example.com/pass
+.Ed
+.Pp
+Then, to retrieve credentials to login as
+.Ar myuser
+in
+.Ar example.com
+you can run
+.Bd -literal -offset indent
+sekrit get accounts/example.com/user | xclip -l 1 -sel clip -q
+sekrit get accounts/example.com/pass | xclip -l 1 -sel clip -q
+.Ed
+.Sh AUTHORS
+.An Lucas
+.Sh LICENSE
+.Nm
+is in the public domain.
+.Pp
+To the extent possible under law, the author(s) have dedicated all
+copyright and related and neighboring rights to this software to the
+public domain worldwide.
+.Pp
+.Lk http://creativecommons.org/publicdomain/zero/1.0/
diff --git a/bin/sekrit.sh b/bin/sekrit.sh
new file mode 100644
index 0000000..b18d0bd
--- /dev/null
+++ b/bin/sekrit.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+# sekrit
+# Written in 2018-2019 by Lucas
+# CC0 1.0 Universal/Public domain - No rights reserved
+#
+# To the extent possible under law, the author(s) have dedicated all
+# copyright and related and neighboring rights to this software to the
+# public domain worldwide. This software is distributed without any
+# warranty. You should have received a copy of the CC0 Public Domain
+# Dedication along with this software. If not, see
+# .
+
+# sekrit is a relatively small shell script for writing and reading
+# encrypted files, aimed to deal mostly with accounts credentials, but
+# general enough to deal with any content. It can populate each key
+# with random data or with fixed data, reading either command-line
+# arguments or stdin.
+#
+# To be used as a password manager, it's recommended to pair it with
+# xclip(1). For example, for username, password and optional second
+# factor:
+#
+# sekrit get account/user | xclip -r -l 1 -sel clip -q
+# sekrit get account/pass | xclip -r -l 1 -sel clip -q
+# sekrit has account/2fa && sekrit get account/2fa \
+# | $program_for_totp | xclip -r -l 1 -sel clip -q
+
+usage()
+{
+ cat - <<. >&2
+Usage:
+ ${0##*/} add key [value ...]
+ ${0##*/} gen [-l length] [chars]
+ ${0##*/} get key
+ ${0##*/} has key
+ ${0##*/} ls [keys ...]
+
+If no value was provided on command line, add reads from stdin.
+.
+ exit 1
+}
+
+err()
+{
+ printf "%s: %s\n" "${0##*/}" "$*" >&2
+ exit 1
+}
+
+check_key()
+{
+ [ "$1" = "${1%/}" ] || err "Key can not end in a slash."
+}
+
+to_number()
+{
+ printf "%u" "$*" 2>/dev/null
+}
+
+sekrit_add()
+{
+ [ $# -eq 1 ] && [ -n "$1" ] || usage
+ key=$1
+ check_key "$key"
+ shift
+
+ f=$SEKRIT_DIR/$key.gpg
+ mkdir -p "${f%/*}"
+
+ [ -f "$f" ] && err "Key \"$key\" already exists."
+
+ if [ $# -ne 0 ]; then
+ printf "%s\n" "$*"
+ else
+ cat -
+ fi | gpg2 -qae -r "$SEKRIT_GPG_ID" >"$f"
+ # make it read-only
+ chmod -- 400 "$f"
+}
+
+sekrit_gen()
+{
+ len=43
+ OPTIND=1
+ while getopts l: flag; do
+ case "$flag" in
+ l) len=$(to_number "$OPTARG") ||
+ err "Invalid password length."
+ ;;
+ [?]) usage
+ ;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+
+ [ $# -le 1 ] || usage
+
+ chars=+/0-9A-Za-z
+ if [ $# -eq 1 ]; then
+ [ -n "$1" ] || usage
+ chars=$1
+ fi
+
+ tr -cd -- "$chars" /dev/null &&
+ printf "\n"
+}
+
+sekrit_get()
+{
+ [ $# -eq 1 ] || usage
+ key=$1
+ check_key "$1"
+ shift
+
+ f=$SEKRIT_DIR/$key.gpg
+ [ ! -f "$f" ] && err "No data for key \"$key\"."
+ gpg2 -qd "$f"
+}
+
+sekrit_has()
+{
+ [ $# -eq 1 ] || usage
+ key=$1
+ check_key "$1"
+ shift
+
+ [ -f "$SEKRIT_DIR/$key.gpg" ]
+}
+
+ls_key()
+{
+ d=$SEKRIT_DIR$1
+ find "$d" -type f -name "*.gpg" | sort | cut -c $((${#d} + 1))- |
+ sed "s/\.gpg$//"
+}
+
+sekrit_ls()
+{
+ if [ $# -eq 0 ]; then
+ ls_key /
+ else
+ for key; do
+ printf "%s:\n" "$key"
+ ls_key /"$key"/ | sed "s/^/ /"
+ printf "\n"
+ done
+ fi
+}
+
+set -e
+
+[ $# -ge 1 ] || usage
+cmd=$1
+shift
+
+umask 077
+: ${SEKRIT_DIR:=~/keep/sekrit}
+: ${SEKRIT_GPG_ID:=myself}
+mkdir -p "$SEKRIT_DIR"
+
+case "$cmd" in
+add) sekrit_add "$@";;
+gen) sekrit_gen "$@" ;;
+get) sekrit_get "$@" ;;
+has) sekrit_has "$@" ;;
+ls) sekrit_ls "$@" ;;
+*) usage ;;
+esac