installer: welcome
diff --git a/core/installer/cmd/welcome.go b/core/installer/cmd/welcome.go
new file mode 100644
index 0000000..3b834b0
--- /dev/null
+++ b/core/installer/cmd/welcome.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+	"fmt"
+	"golang.org/x/crypto/ssh"
+	"net"
+	"os"
+
+	"github.com/go-git/go-billy/v5/memfs"
+	"github.com/go-git/go-git/v5"
+	gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
+	"github.com/go-git/go-git/v5/storage/memory"
+	"github.com/spf13/cobra"
+
+	"github.com/giolekva/pcloud/core/installer"
+	"github.com/giolekva/pcloud/core/installer/welcome"
+)
+
+var welcomeFlags struct {
+	repo   string
+	sshKey string
+	port   int
+}
+
+func welcomeCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:  "welcome",
+		RunE: welcomeCmdRun,
+	}
+	cmd.Flags().StringVar(
+		&welcomeFlags.repo,
+		"repo-addr",
+		"",
+		"",
+	)
+	cmd.Flags().StringVar(
+		&welcomeFlags.sshKey,
+		"ssh-key",
+		"",
+		"",
+	)
+	cmd.Flags().IntVar(
+		&welcomeFlags.port,
+		"port",
+		8080,
+		"",
+	)
+	return cmd
+}
+
+func welcomeCmdRun(cmd *cobra.Command, args []string) error {
+	sshKey, err := os.ReadFile(welcomeFlags.sshKey)
+	if err != nil {
+		return err
+	}
+	auth := authSSH(sshKey)
+	repo, err := git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
+		URL:             welcomeFlags.repo,
+		Auth:            auth,
+		RemoteName:      "origin",
+		ReferenceName:   "refs/heads/master",
+		Depth:           1,
+		InsecureSkipTLS: true,
+		Progress:        os.Stdout,
+	})
+	s := welcome.NewServer(
+		welcomeFlags.port,
+		installer.NewRepoIO(repo, auth.Signer),
+	)
+	s.Start()
+	return nil
+}
+
+func authSSH(pemBytes []byte) *gitssh.PublicKeys {
+	a, err := gitssh.NewPublicKeys("git", pemBytes, "")
+	if err != nil {
+		panic(err)
+	}
+	a.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+		// TODO(giolekva): verify server public key
+		fmt.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
+		return nil
+	}
+	return a
+}