diff --git a/cmd/converge/converge.go b/cmd/converge/converge.go
index bae64aa..5c4edf4 100644
--- a/cmd/converge/converge.go
+++ b/cmd/converge/converge.go
@@ -161,6 +161,9 @@ func main() {
 		http.FileServer(http.Dir(downloadDir))))
 	http.HandleFunc("/", catchAllHandler)
 
+	// create usage generator
+	http.HandleFunc("/usage", generateCLIExammple)
+
 	// Start HTTP server
 	fmt.Println("Rendez-vous server listening on :8000")
 	log.Fatal(http.ListenAndServe(":8000", nil))
diff --git a/cmd/converge/convergeaccess.go b/cmd/converge/convergeaccess.go
new file mode 100644
index 0000000..f69407f
--- /dev/null
+++ b/cmd/converge/convergeaccess.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+	"converge/pkg/models"
+	"converge/pkg/server/converge"
+	"net/http"
+	"strings"
+)
+
+func getConvergeAccess(r *http.Request, sshRemoteUser string) models.ConvergeAccess {
+	secure := ""
+	if r.TLS == nil {
+		secure = ""
+	} else {
+		secure = "s"
+	}
+	for _, header := range []string{"X-Forwarded-Proto", "X-Scheme", "X-Forwarded-Scheme"} {
+		values := r.Header.Values(header)
+		for _, value := range values {
+			if strings.ToLower(value) == "https" {
+				secure = "s"
+			}
+		}
+	}
+
+	location, err := converge.GetUserLocation(r)
+	if err != nil {
+		panic(err)
+	}
+	return models.ConvergeAccess{
+		Secure:   secure,
+		HostPort: r.Host,
+		Location: location,
+		Username: sshRemoteUser,
+	}
+}
diff --git a/cmd/converge/pagehandler.go b/cmd/converge/pagehandler.go
index e0909cc..bdc256e 100644
--- a/cmd/converge/pagehandler.go
+++ b/cmd/converge/pagehandler.go
@@ -1,34 +1,14 @@
 package main
 
 import (
-	"converge/pkg/server/converge"
 	templates2 "converge/pkg/server/templates"
 	"net/http"
 	"os"
-	"strings"
 )
 
 func pageHandler(w http.ResponseWriter, r *http.Request) {
-	secure := ""
-	if r.TLS == nil {
-		secure = ""
-	} else {
-		secure = "s"
-	}
-	for _, header := range []string{"X-Forwarded-Proto", "X-Scheme", "X-Forwarded-Scheme"} {
-		values := r.Header.Values(header)
-		for _, value := range values {
-			if strings.ToLower(value) == "https" {
-				secure = "s"
-			}
-		}
-	}
 	username, _ := os.LookupEnv("CONVERGE_USERNAME")
-
-	location, err := converge.GetUserLocation(r)
-	if err != nil {
-		panic(err)
-	}
+	access := getConvergeAccess(r, username)
 
 	switch r.URL.Path {
 	case "":
@@ -37,12 +17,13 @@ func pageHandler(w http.ResponseWriter, r *http.Request) {
 		fallthrough
 	case "index.html":
 		templates2.AboutTab().Render(r.Context(), w)
+		// TODO use contexts later.
 	case "usage.html":
-		templates2.UsageTab(secure, r.Host, username).Render(r.Context(), w)
+		templates2.UsageTab(access).Render(r.Context(), w)
 	case "downloads.html":
 		templates2.DownloadsTab().Render(r.Context(), w)
 	case "sessions.html":
-		templates2.SessionsTab(nil, location).Render(r.Context(), w)
+		templates2.SessionsTab(nil, access.Location).Render(r.Context(), w)
 	default:
 		http.NotFound(w, r)
 	}
diff --git a/cmd/converge/usage.go b/cmd/converge/usage.go
new file mode 100644
index 0000000..63304ff
--- /dev/null
+++ b/cmd/converge/usage.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+	"log"
+	"net/http"
+	"time"
+)
+
+func generateCLIExammple(w http.ResponseWriter, r *http.Request) {
+	log.Println("usage: got ", r.URL.Path)
+
+	err := r.ParseForm()
+	if err != nil {
+		http.Error(w, "Error parsing form", http.StatusBadRequest)
+		return
+	}
+	remote_shells := r.Form["remote-shell"]
+	local_shells := r.Form["local-shhell"]
+	keys := r.FormValue("ssh-keys")
+	log.Printf("remote_shells %v", remote_shells)
+	log.Printf("local_shells %v", local_shells)
+	log.Printf("ssh-keys %v", keys)
+	w.Write([]byte(time.Now().Format(time.DateTime)))
+}
diff --git a/cmd/templaterender/render.go b/cmd/templaterender/render.go
index 01bffee..3ea7024 100644
--- a/cmd/templaterender/render.go
+++ b/cmd/templaterender/render.go
@@ -37,11 +37,18 @@ func main() {
 		panic(err)
 	}
 
+	access := models.ConvergeAccess{
+		Secure:   "s",
+		HostPort: "example.com",
+		Location: netherlands,
+		Username: "converge",
+	}
+
 	fullindex := func() templ.Component {
-		return templates2.Index("s", "example.com", "converge")
+		return templates2.Index(access)
 	}
 	usage := func() templ.Component {
-		return templates2.UsageTab("s", "example.com", "converge")
+		return templates2.UsageTab(access)
 	}
 
 	render(dir, "fullindex.html", fullindex)
diff --git a/pkg/models/convergeaccess.go b/pkg/models/convergeaccess.go
new file mode 100644
index 0000000..007c321
--- /dev/null
+++ b/pkg/models/convergeaccess.go
@@ -0,0 +1,11 @@
+package models
+
+import "time"
+
+type ConvergeAccess struct {
+	// 's" when secure, "" otherwise
+	Secure   string
+	HostPort string
+	Location *time.Location
+	Username string
+}
diff --git a/pkg/server/templates/index.templ b/pkg/server/templates/index.templ
index 8914d90..947e97e 100644
--- a/pkg/server/templates/index.templ
+++ b/pkg/server/templates/index.templ
@@ -1,9 +1,12 @@
 package templates
 
-templ Index(secure string, host string, username string) {
+import "converge/pkg/models"
+
+
+templ Index(access models.ConvergeAccess) {
     @BasePage(0) {
                     @About()
-                    @Usage(secure, host, username)
+                    @Usage(access)
                     @Downloads()
     }
 }
diff --git a/pkg/server/templates/usage.templ b/pkg/server/templates/usage.templ
index 252f746..4fe25d8 100644
--- a/pkg/server/templates/usage.templ
+++ b/pkg/server/templates/usage.templ
@@ -1,8 +1,62 @@
 package templates
 
-templ Usage(secure string, host string, username string) {
+import "converge/pkg/models"
+
+templ Usage(access models.ConvergeAccess) {
   <div>
-                      <h1>usage</h1>
+
+
+      <h1>Usage</h1>
+
+      <style>
+      .minimal-width {
+        width: 1%;
+        white-space: nowrap;
+      }
+      </style>
+
+      <form id="inputs" novalidate
+            hx-post="/usage"
+            method="post"
+            hx-trigger="load,input delay:500ms,change"
+            hx-target="#example-cli"
+            hx-on::after-request="saveFormToCookie()">
+          <table class="table table-responsive">
+               <tr>
+                 <td class="minimal-width"><label for="rendez-vous-id">rendez-vous id</label></td>
+                 <td>
+                   <input id="rendez-vous-id" class="form-control" name="rendez-vous-id" type="text" maxlength="40"></input>
+                 </td>
+               </tr>
+               <tr>
+                 <td class="minimal-width"><label for="ssh-keys">public ssh keys</label></td>
+                 <td>
+                   <textarea id="ssh-keys" class="form-control autogrow" name="ssh-keys" rows="5"></textarea>
+                 </td>
+              </tr>
+              <tr>
+                  <td class="minimal-width"><label for="remote-shell">remote shells</label></td>
+                  <td>
+                      <input checked id="remote-shell-0" name="remote-shell" type="checkbox" value="*sh"> <label for="remote-shell-0">*.sh</label>
+                      <input id="remote-shell-1" name="remote-shell" type="checkbox" value="cmd"> <label for="remote-shell-1">cmd.exe</label>
+                      <input id="remote-shell-2" name="remote-shell" type="checkbox" value="cmd"> <label for="remote-shell-2">powerpoint.exe</label>
+                  </td>
+              </tr>
+              <tr>
+                  <td class="minimal-width"><label for="local-shell">local shell</label></td>
+                  <td>
+                      <input id="checked local-shell-0" name="local-shell" type="checkbox" value="*sh"> <label for="local-shell-0">*.sh</label>
+                      <input id="local-shell-1" name="local-shell" type="checkbox" value="cmd"> <label for="local-shell-1">cmd.exe</label>
+                      <input id="local-shell-2" name="local-shell" type="checkbox" value="cmd"> <label for="local-shell-2">powerpoint.exe</label>
+                  </td>
+              </tr>
+          </table>
+      </form>
+
+      <div id="example-cli">
+      </div>
+
+                      <h1>usage old</h1>
 
                       <h2>Continuous integration jobs</h2>
 
@@ -12,14 +66,14 @@ templ Usage(secure string, host string, username string) {
                       </p>
                       <pre>{`
            # linux
-           `}curl http{secure}://{host}/static/agent > agent{`
+           `}curl http{access.Secure}://{access.HostPort}/static/agent > agent{`
            chmod 755 agent
-           `}./agent --id ID ws{secure}://{host}{`
+           `}./agent --id ID ws{access.Secure}://{access.HostPort}{`
            rm -f agent
 
            # windows
-           `}curl http{secure}://{host}/static/agent.exe > agent.exe{`
-           `}agent --id ID ws{secure}://{host}{`
+           `}curl http{access.Secure}://{access.HostPort}/static/agent.exe > agent.exe{`
+           `}agent --id ID ws{access.Secure}://{access.HostPort}{`
            del agent.exe
           `}</pre>
                       <p>
@@ -66,8 +120,8 @@ templ Usage(secure string, host string, username string) {
 
                       <pre>
                                                                                                                               {`
-          `}ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{secure}://{host}/client/ID"  { username }{"@localhost"}   {`
-          `}sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{secure}://{host}/client/ID" { username }{"@localhost"}   {`
+          `}ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.HostPort}/client/ID"  { access.Username }{"@localhost"}   {`
+          `}sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.HostPort}/client/ID" { access.Username }{"@localhost"}   {`
           `}</pre>
 
                       <h2>Local clients: using SSH with a local TCP forwarding proxy</h2>
@@ -91,8 +145,8 @@ templ Usage(secure string, host string, username string) {
                       </p>
 
                       <pre>                                                           {`
-          `}ssh -oServerAliveInterval=10 -p 10000 { username }{"@localhost"}          {`
-          `}sftp -oServerAliveInterval=10 -oPort=10000 { username }{"@localhost"}     {`
+          `}ssh -oServerAliveInterval=10 -p 10000 { access.Username }{"@localhost"}          {`
+          `}sftp -oServerAliveInterval=10 -oPort=10000 { access.Username }{"@localhost"}     {`
           `}</pre>
 
                       <h2>Remote shell usage</h2>
@@ -145,7 +199,7 @@ templ Usage(secure string, host string, username string) {
                       <h2>Authentication</h2>
 
                       <p>
-                          The <code>{ username }</code> user above is configured in the
+                          The <code>{ access.Username }</code> user above is configured in the
                           Converge server and is communicated to the agent when the agent is
                           started as well as the password.
                       </p>
@@ -168,13 +222,91 @@ templ Usage(secure string, host string, username string) {
                           Note that on windows you should not used quotes.
                       </p>
 
+
+    <script>
+    function setCookie(name, value, days) {
+        let expires = "";
+        if (days) {
+            const date = new Date();
+            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
+            expires = "; expires=" + date.toUTCString();
+        }
+        document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/";
+    }
+
+    function getCookie(name) {
+        const nameEQ = name + "=";
+        const ca = document.cookie.split(';');
+        for(let i=0; i < ca.length; i++) {
+            let c = ca[i];
+            while (c.charAt(0)==' ') c = c.substring(1,c.length);
+            if (c.indexOf(nameEQ) == 0) return decodeURIComponent(c.substring(nameEQ.length,c.length));
+        }
+        return null;
+    }
+
+    function saveFormToCookie() {
+        const form = document.getElementById('inputs');
+        const formData = new FormData(form);
+        for (let element of form.elements) {
+            if (element.id) {
+                console.log("Saving " + element.id)
+                if (element.type === 'checkbox') {
+                    console.log("Checkbox " + element.checked)
+                    setCookie(element.id, element.checked ? 'true' : 'false', 7);
+                } else if (element.type === 'radio') {
+                    if (element.checked) {
+                        setCookie(element.id, element.value, 7);
+                    }
+                } else {
+                    setCookie(element.id, element.value, 7);
+                }
+            }
+        }
+    }
+
+    function loadFormFromCookie() {
+        const form = document.getElementById('inputs');
+        for (let element of form.elements) {
+            if (element.id) {
+                const value = getCookie(element.id);
+                console.log("Loading " + element.id + " value: " + value)
+                if (value !== null) {
+                    if (element.type === 'checkbox') {
+                        element.checked = value === 'true';
+                    } else if (element.type === 'radio') {
+                        element.checked = (element.value === value);
+                    } else {
+                        element.value = value;
+                    }
+                }
+            }
+        }
+    }
+
+    // Save form data to cookie on change
+    document.getElementById('inputs').addEventListener('change', saveFormToCookie);
+
+    document.body.addEventListener('htmx:load', function(event) {
+      loadFormFromCookie();
+    });
+
+    document.body.addEventListener('htmx:afterSettle', function(event) {
+      loadFormFromCookie();
+    });
+
+    // when hx-boost=false
+    // Load form data from cookie on page load
+    //document.addEventListener('DOMContentLoaded', loadFormFromCookie);
+    </script>
+
   </div>
 }
 
 
 
-templ UsageTab(secure string, host string, username string) {
+templ UsageTab(access models.ConvergeAccess) {
     @BasePage(2) {
-                    @Usage(secure, host, username)
+                    @Usage(access)
     }
 }