diff --git a/.gitignore b/.gitignore
index 835fb56..831dc4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,7 @@
 cpu.out
 vendor
-cmd/cmd*
 cmd/jess*
 dist
 
-# Custom dev deops
+# Custom dev deps
 go.mod.*
diff --git a/cmd/cmd-import-export.go b/cmd/cmd-import-export.go
new file mode 100644
index 0000000..f161899
--- /dev/null
+++ b/cmd/cmd-import-export.go
@@ -0,0 +1,170 @@
+package main
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/spf13/cobra"
+
+	"github.com/safing/jess"
+)
+
+func init() {
+	rootCmd.AddCommand(exportCmd)
+	rootCmd.AddCommand(backupCmd)
+	rootCmd.AddCommand(importCmd)
+}
+
+var (
+	exportCmdHelp = "usage: export <id>"
+	exportCmd     = &cobra.Command{
+		Use:   "export <id>",
+		Short: "export a signet or envelope",
+		Long:  "export a signet (as a recipient - the public key only) or an envelope (configuration)",
+		RunE:  handleExport,
+	}
+
+	backupCmdHelp = "usage: backup <id"
+	backupCmd     = &cobra.Command{
+		Use:   "backup <id>",
+		Short: "backup a signet",
+		Long:  "backup a signet (the private key - do not share!)",
+		RunE:  handleBackup,
+	}
+
+	importCmdHelp = "usage: import <text>"
+	importCmd     = &cobra.Command{
+		Use:   "import <text>",
+		Short: "import a signet or an enveleope",
+		Long:  "import a signet (any kind) or an enveleope",
+		RunE:  handleImport,
+	}
+)
+
+func handleExport(cmd *cobra.Command, args []string) error {
+	// Check args.
+	if len(args) != 1 {
+		return errors.New(exportCmdHelp)
+	}
+	id := args[0]
+
+	// Get Recipient.
+	recipient, err := trustStore.GetSignet(id, true)
+	if err == nil {
+		text, err := recipient.Export(false)
+		if err != nil {
+			return fmt.Errorf("failed to export recipient %s: %w", id, err)
+		}
+		fmt.Println(text)
+		return nil
+	}
+
+	// Check if there is a signet instead.
+	signet, err := trustStore.GetSignet(id, false)
+	if err == nil {
+		recipient, err := signet.AsRecipient()
+		if err != nil {
+			return fmt.Errorf("failed convert signet %s to recipient for export: %w", id, err)
+		}
+		text, err := recipient.Export(false)
+		if err != nil {
+			return fmt.Errorf("failed to export recipient %s: %w", id, err)
+		}
+		fmt.Println(text)
+		return nil
+	}
+
+	// Check for an envelope.
+	env, err := trustStore.GetEnvelope(id)
+	if err == nil {
+		text, err := env.Export(false)
+		if err != nil {
+			return fmt.Errorf("failed to export envelope %s: %w", id, err)
+		}
+		fmt.Println(text)
+		return nil
+	}
+
+	return errors.New("no recipient or envelope found with the given ID")
+}
+
+func handleBackup(cmd *cobra.Command, args []string) error {
+	// Check args.
+	if len(args) != 1 {
+		return errors.New(backupCmdHelp)
+	}
+	id := args[0]
+
+	// Check if there is a signet instead.
+	signet, err := trustStore.GetSignet(id, false)
+	if err != nil {
+		text, err := signet.Backup(false)
+		if err != nil {
+			return fmt.Errorf("failed to backup signet %s: %w", id, err)
+		}
+		fmt.Println(text)
+		return nil
+	}
+
+	return errors.New("no signet found with the given ID")
+}
+
+func handleImport(cmd *cobra.Command, args []string) error {
+	// Check args.
+	if len(args) != 1 {
+		return errors.New(importCmdHelp)
+	}
+	text := args[0]
+
+	// First, check if it's an envelope.
+	if strings.HasPrefix(text, jess.ExportEnvelopePrefix) {
+		env, err := jess.EnvelopeFromTextFormat(text)
+		if err != nil {
+			return fmt.Errorf("failed to parse envelope: %w", err)
+		}
+		err = trustStore.StoreEnvelope(env)
+		if err != nil {
+			return fmt.Errorf("failed to import envelope into trust store: %w", err)
+		}
+		fmt.Printf("imported envelope %q intro trust store\n", env.Name)
+		return nil
+	}
+
+	// Then handle all signet types together.
+	var (
+		signetType string
+		parseFunc  func(textFormat string) (*jess.Signet, error)
+	)
+	switch {
+	case strings.HasPrefix(text, jess.ExportSenderPrefix):
+		signetType = jess.ExportSenderKeyword
+		parseFunc = jess.SenderFromTextFormat
+	case strings.HasPrefix(text, jess.ExportRecipientPrefix):
+		signetType = jess.ExportRecipientKeyword
+		parseFunc = jess.RecipientFromTextFormat
+	case strings.HasPrefix(text, jess.ExportKeyPrefix):
+		signetType = jess.ExportKeyKeyword
+		parseFunc = jess.KeyFromTextFormat
+	default:
+		return fmt.Errorf(
+			"invalid format or unknown type, expected one of %s, %s, %s, %s",
+			jess.ExportKeyKeyword,
+			jess.ExportSenderKeyword,
+			jess.ExportRecipientKeyword,
+			jess.ExportEnvelopeKeyword,
+		)
+	}
+	// Parse and import
+	signet, err := parseFunc(text)
+	if err != nil {
+		return fmt.Errorf("failed to parse %s: %w", signetType, err)
+	}
+	err = trustStore.StoreSignet(signet)
+	if err != nil {
+		return fmt.Errorf("failed to import %s into trust store: %w", signetType, err)
+	}
+	fmt.Printf("imported %s %s intro trust store\n", signetType, signet.ID)
+
+	return nil
+}
diff --git a/cmd/cmd-sign.go b/cmd/cmd-sign.go
new file mode 100644
index 0000000..0db49c1
--- /dev/null
+++ b/cmd/cmd-sign.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/spf13/cobra"
+
+	"github.com/safing/jess/filesig"
+)
+
+func init() {
+	rootCmd.AddCommand(signCmd)
+	signCmd.Flags().StringVarP(&closeFlagOutput, "output", "o", "", "specify output file (`-` for stdout")
+	signCmd.Flags().StringToStringVarP(&metaDataFlag, "metadata", "m", nil, "specify file metadata to sign")
+}
+
+var (
+	metaDataFlag map[string]string
+	signCmdHelp  = "usage: jess sign <file> with <envelope name>"
+
+	signCmd = &cobra.Command{
+		Use:                   "sign <file> with <envelope name>",
+		Short:                 "sign file",
+		Long:                  "sign file with the given envelope. Use `-` to use stdin",
+		DisableFlagsInUseLine: true,
+		PreRunE:               requireTrustStore,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			registerPasswordCallbacks()
+
+			// check args
+			if len(args) != 3 || args[1] != "with" {
+				return errors.New(signCmdHelp)
+			}
+
+			// get envelope
+			envelope, err := trustStore.GetEnvelope(args[2])
+			if err != nil {
+				return err
+			}
+
+			// check filenames
+			filename := args[0]
+			outputFilename := closeFlagOutput
+			if outputFilename == "" {
+				if strings.HasSuffix(filename, filesig.Extension) {
+					return errors.New("cannot automatically derive output filename, please specify with --output")
+				}
+				outputFilename = filename + filesig.Extension
+			}
+
+			fd, err := filesig.SignFile(filename, outputFilename, metaDataFlag, envelope, trustStore)
+			if err != nil {
+				return err
+			}
+
+			fmt.Print(formatSignatures(filename, outputFilename, []*filesig.FileData{fd}))
+			return nil
+		},
+	}
+)