client-android: store network config as app prefs
diff --git a/charts/vpn-mesh-config/templates/lighthouse-config-template.yaml b/charts/vpn-mesh-config/templates/lighthouse-config-template.yaml
index 016ecb0..f76f526 100644
--- a/charts/vpn-mesh-config/templates/lighthouse-config-template.yaml
+++ b/charts/vpn-mesh-config/templates/lighthouse-config-template.yaml
@@ -28,7 +28,7 @@
       drop_local_broadcast: false
       drop_multicast: false
       tx_queue: 500
-      mtu: 1300
+      mtu: 576
     logging:
       level: debug
       format: text
diff --git a/charts/vpn-mesh-config/templates/lighthouse-service.yaml b/charts/vpn-mesh-config/templates/lighthouse-service.yaml
index 267d0f5..b23d99d 100644
--- a/charts/vpn-mesh-config/templates/lighthouse-service.yaml
+++ b/charts/vpn-mesh-config/templates/lighthouse-service.yaml
@@ -5,6 +5,7 @@
   namespace: {{ .Release.Namespace }}
 spec:
   type: LoadBalancer
+  externalTrafficPolicy: Local
   selector:
     app.kubernetes.io/name: ingress-nginx
     app.kubernetes.io/instance: ingress-private
diff --git a/core/client/android/app/build.gradle b/core/client/android/app/build.gradle
index 9102497..6a8c04f 100644
--- a/core/client/android/app/build.gradle
+++ b/core/client/android/app/build.gradle
@@ -47,4 +47,5 @@
     implementation 'androidx.camera:camera-core:1.1.0-alpha11'
     implementation 'androidx.camera:camera-lifecycle:1.1.0-alpha11'
     implementation 'androidx.camera:camera-view:1.0.0-alpha31'
+    implementation "androidx.security:security-crypto:1.1.0-alpha03"
 }
\ No newline at end of file
diff --git a/core/client/android/app/src/main/AndroidManifest.xml b/core/client/android/app/src/main/AndroidManifest.xml
index a75444d..ccdff2c 100644
--- a/core/client/android/app/src/main/AndroidManifest.xml
+++ b/core/client/android/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
 
     <application
         android:name=".PCloudApp"
diff --git a/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudActivity.java b/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudActivity.java
index 330e8cd..5e7dedd 100644
--- a/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudActivity.java
+++ b/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudActivity.java
@@ -81,18 +81,15 @@
         return null;
     }
 
-    public String startVpn(String ipCidr) {
+    public void startVpn() {
         Intent intent = VpnService.prepare(this);
         if (intent != null) {
-            System.out.println("#### STARTVPN");
             intent.setAction(PCloudVPNService.ACTION_CONNECT);
             startActivityForResult(intent, VPN_START_CODE);
         } else {
             intent = new Intent(this, PCloudVPNService.class);
             startService(intent);
         }
-
-        return null;
     }
 
     private native void qrcodeScanned(String contents);
diff --git a/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudApp.java b/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudApp.java
index 1e2b64a..b7647db 100644
--- a/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudApp.java
+++ b/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudApp.java
@@ -1,13 +1,66 @@
 package me.lekva.pcloud;
 
 import android.app.Application;
+import android.app.NotificationChannel;
+import android.content.SharedPreferences;
+import android.os.Build;
+
+import androidx.core.app.NotificationManagerCompat;
+import androidx.security.crypto.EncryptedSharedPreferences;
+import androidx.security.crypto.MasterKey;
 
 import org.gioui.Gio;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+
 public class PCloudApp extends Application {
+    static final String STATUS_CHANNEL_ID = "pcloud-status";
+    static final int STATUS_NOTIFICATION_ID = 1;
+
+    static final String NOTIFY_CHANNEL_ID = "pcloud-notify";
+    static final int NOTIFY_NOTIFICATION_ID = 2;
+
     @Override
     public void onCreate() {
         super.onCreate();
         Gio.init(this);
+
+        createNotificationChannel(NOTIFY_CHANNEL_ID, "Notifications", NotificationManagerCompat.IMPORTANCE_DEFAULT);
+        createNotificationChannel(STATUS_CHANNEL_ID, "VPN Status", NotificationManagerCompat.IMPORTANCE_LOW);
+    }
+
+    private void createNotificationChannel(String id, String name, int importance) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            return;
+        }
+        NotificationChannel channel = new NotificationChannel(id, name, importance);
+        NotificationManagerCompat nm = NotificationManagerCompat.from(this);
+        nm.createNotificationChannel(channel);
+    }
+
+    // encryptToPref a byte array of data using the Jetpack Security
+    // library and writes it to a global encrypted preference store.
+    public void encryptToPref(String prefKey, String plaintext) throws IOException, GeneralSecurityException {
+        getEncryptedPrefs().edit().putString(prefKey, plaintext).commit();
+    }
+
+    // decryptFromPref decrypts a encrypted preference using the Jetpack Security
+    // library and returns the plaintext.
+    public String decryptFromPref(String prefKey) throws IOException, GeneralSecurityException {
+        return getEncryptedPrefs().getString(prefKey, null);
+    }
+
+    private SharedPreferences getEncryptedPrefs() throws IOException, GeneralSecurityException {
+        MasterKey key = new MasterKey.Builder(this)
+                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+                .build();
+        return EncryptedSharedPreferences.create(
+                this,
+                "secret_shared_prefs",
+                key,
+                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+        );
     }
 }
diff --git a/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudVPNService.java b/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudVPNService.java
index 1566ed1..91a4a38 100644
--- a/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudVPNService.java
+++ b/core/client/android/app/src/main/java/me/lekva/pcloud/PCloudVPNService.java
@@ -10,11 +10,15 @@
 import android.system.OsConstants;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
 
 public class PCloudVPNService extends VpnService implements Handler.Callback {
     public static final String ACTION_CONNECT = "CONNECT";
     public static final String ACTION_DISCONNECT = "DISCONNECT";
 
+    private PendingIntent configureIntent;
+
     private boolean running = false;
     private Handler handler = null;
 
@@ -23,6 +27,9 @@
         if (handler == null) {
             handler = new Handler(this);
         }
+
+        configureIntent = PendingIntent.getActivity(this, 0, new Intent(this, PCloudActivity.class),
+                PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     @Override
@@ -41,8 +48,10 @@
         stopVpn();
     }
 
+    @RequiresApi(api = Build.VERSION_CODES.O)
     private void startVpn() {
         System.out.println("--- START");
+        updateForegroundNotification();
         connect();
     }
 
@@ -73,6 +82,15 @@
         return builder;
     }
 
+    private void updateForegroundNotification() {
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, PCloudApp.STATUS_CHANNEL_ID)
+                .setContentTitle("PCloud")
+                .setContentText("hiiii")
+                .setContentIntent(configureIntent)
+                .setPriority(NotificationCompat.PRIORITY_LOW);
+        startForeground(PCloudApp.STATUS_NOTIFICATION_ID, builder.build());
+    }
+
     private native void connect();
     private native void disconnect();
 }
diff --git a/core/client/cmd/pcloud/app.go b/core/client/cmd/pcloud/app.go
index 0f0dc47..e54710b 100644
--- a/core/client/cmd/pcloud/app.go
+++ b/core/client/cmd/pcloud/app.go
@@ -5,6 +5,8 @@
 type App interface {
 	LaunchBarcodeScanner() error
 	OnView(app.ViewEvent) error
-	StartVPN(config []byte) error
-	Connect(service interface{}) error
+	UpdateService(service interface{}) error
+	TriggerService() error
+	Connect(config Config) error
+	CreateStorage() Storage
 }
diff --git a/core/client/cmd/pcloud/app_android.go b/core/client/cmd/pcloud/app_android.go
index 61737ee..ce90588 100644
--- a/core/client/cmd/pcloud/app_android.go
+++ b/core/client/cmd/pcloud/app_android.go
@@ -14,12 +14,11 @@
 )
 
 type androidApp struct {
-	jvm          *jni.JVM
-	appCtx       jni.Object // PCloudApp
-	activity     jni.Object // PCloudActivity
-	service      jni.Object // PCloudVPNService
-	nebulaConfig []byte
-	ctrl         *nebula.Control
+	jvm      *jni.JVM
+	appCtx   jni.Object // PCloudApp
+	activity jni.Object // PCloudActivity
+	service  jni.Object // PCloudVPNService
+	ctrl     *nebula.Control
 }
 
 func createApp() App {
@@ -29,6 +28,10 @@
 	}
 }
 
+func (a *androidApp) CreateStorage() Storage {
+	return CreateStorage(a.jvm, a.appCtx)
+}
+
 func (a *androidApp) LaunchBarcodeScanner() error {
 	return jni.Do(a.jvm, func(env *jni.Env) error {
 		cls := jni.GetObjectClass(env, a.activity)
@@ -82,24 +85,20 @@
 	return ctx, nil
 }
 
-func (a *androidApp) StartVPN(config []byte) error {
-	a.nebulaConfig = config
+func (a *androidApp) TriggerService() error {
 	return jni.Do(a.jvm, func(env *jni.Env) error {
 		cls := jni.GetObjectClass(env, a.activity)
-		m := jni.GetMethodID(env, cls, "startVpn", "(Ljava/lang/String;)Ljava/lang/String;")
-		jConfig := jni.JavaString(env, string(config))
-		_, err := jni.CallObjectMethod(env, a.activity, m, jni.Value(jConfig))
-		return err
-
+		m := jni.GetMethodID(env, cls, "startVpn", "()V")
+		return jni.CallVoidMethod(env, a.activity, m)
 	})
 }
 
-func (a *androidApp) Connect(serv interface{}) error {
+func (a *androidApp) UpdateService(serv interface{}) error {
 	s, ok := serv.(jni.Object)
 	if !ok {
 		return fmt.Errorf("Unexpected service type: %T", serv)
 	}
-	jni.Do(a.jvm, func(env *jni.Env) error {
+	return jni.Do(a.jvm, func(env *jni.Env) error {
 		if jni.IsSameObject(env, s, a.service) {
 			// We already have a reference.
 			jni.DeleteGlobalRef(env, s)
@@ -132,18 +131,17 @@
 		a.service = s
 		return nil
 	})
-	return a.buildVPNConfigurationAndConnect()
 }
 
-func (a *androidApp) buildVPNConfigurationAndConnect() error {
-	if string(a.nebulaConfig) == "" {
+func (a *androidApp) Connect(config Config) error {
+	if config.Network == nil {
 		return nil
 	}
-	config := nc.NewC(logrus.StandardLogger())
-	if err := config.LoadString(string(a.nebulaConfig)); err != nil {
+	nebulaConfig := nc.NewC(logrus.StandardLogger())
+	if err := nebulaConfig.LoadString(string(config.Network)); err != nil {
 		return err
 	}
-	pki := config.GetMap("pki", nil)
+	pki := nebulaConfig.GetMap("pki", nil)
 	hostCert, _, err := cert.UnmarshalNebulaCertificateFromPEM([]byte(pki["cert"].(string)))
 	if err != nil {
 		panic(err)
@@ -177,7 +175,7 @@
 				jni.Value(jni.JavaString(env, ip)),
 				jni.Value(jni.Value(prefix)))
 		}
-		tun := config.GetMap("tun", nil)
+		tun := nebulaConfig.GetMap("tun", nil)
 		setMtu := jni.GetMethodID(env, bcls, "setMtu", "(I)Landroid/net/VpnService$Builder;")
 		if _, err := jni.CallObjectMethod(env, b, setMtu, jni.Value(tun["mtu"].(int))); err != nil {
 			return err
@@ -205,7 +203,7 @@
 			return err
 		}
 		fd := int(tunFD)
-		ctrl, err := nebula.Main(config, false, "pcloud", logrus.StandardLogger(), &fd)
+		ctrl, err := nebula.Main(nebulaConfig, false, "pcloud", logrus.StandardLogger(), &fd)
 		if err != nil {
 			return err
 		}
diff --git a/core/client/cmd/pcloud/app_darwin.go b/core/client/cmd/pcloud/app_darwin.go
index c7b670f..28532d0 100644
--- a/core/client/cmd/pcloud/app_darwin.go
+++ b/core/client/cmd/pcloud/app_darwin.go
@@ -21,10 +21,18 @@
 	return nil
 }
 
-func (a *darwinApp) StartVPN(config []byte) error {
+func (a *darwinApp) Connect(config Config) error {
 	return nil
 }
 
-func (a *darwinApp) Connect(serv interface{}) error {
+func (a *darwinApp) UpdateService(serv interface{}) error {
+	return nil
+}
+
+func (a *darwinApp) TriggerService() error {
+	return nil
+}
+
+func (a *darwinApp) CreateStorage() Storage {
 	return nil
 }
diff --git a/core/client/cmd/pcloud/client.go b/core/client/cmd/pcloud/client.go
index 0d059ff..6763f17 100644
--- a/core/client/cmd/pcloud/client.go
+++ b/core/client/cmd/pcloud/client.go
@@ -6,7 +6,6 @@
 	"crypto/tls"
 	"encoding/base64"
 	"encoding/json"
-	"errors"
 	"io"
 	"net/http"
 
@@ -16,8 +15,7 @@
 )
 
 type VPNClient interface {
-	Address() string
-	Sign(message []byte) ([]byte, error)
+	Sign(apiAddr string, message []byte) ([]byte, error)
 	Join(apiAddr string, message, signature []byte) ([]byte, error)
 }
 
@@ -29,10 +27,6 @@
 	return &directVPNClient{addr}
 }
 
-func (c *directVPNClient) Address() string {
-	return c.addr
-}
-
 type signReq struct {
 	Message []byte `json:"message"`
 }
@@ -41,7 +35,7 @@
 	Signature []byte `json:"signature"`
 }
 
-func (c *directVPNClient) Sign(message []byte) ([]byte, error) {
+func (c *directVPNClient) Sign(apiAddr string, message []byte) ([]byte, error) {
 	var data bytes.Buffer
 	if err := json.NewEncoder(&data).Encode(signReq{message}); err != nil {
 		return nil, err
@@ -52,7 +46,7 @@
 			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
 		},
 	}
-	r, err := client.Post(c.addr+"/api/sign", "application/json", &data)
+	r, err := client.Post(apiAddr+"/api/sign", "application/json", &data)
 	if err != nil {
 		return nil, err
 	}
@@ -76,9 +70,6 @@
 }
 
 func (c *directVPNClient) Join(apiAddr string, message, signature []byte) ([]byte, error) {
-	if c.addr != "" {
-		return nil, errors.New("Already joined")
-	}
 	pubKey, privKey, err := x25519Keypair()
 	if err != nil {
 		return nil, err
@@ -121,7 +112,6 @@
 		panic("Must not reach")
 	}
 	pki["key"] = string(cert.MarshalX25519PrivateKey(privKey))
-	c.addr = apiAddr
 	return yaml.Marshal(cfgMap)
 }
 
diff --git a/core/client/cmd/pcloud/config.go b/core/client/cmd/pcloud/config.go
new file mode 100644
index 0000000..0cf551d
--- /dev/null
+++ b/core/client/cmd/pcloud/config.go
@@ -0,0 +1,6 @@
+package main
+
+type Config struct {
+	ApiAddr string `json:"api_addr"`
+	Network []byte `json:"network"`
+}
diff --git a/core/client/cmd/pcloud/main.go b/core/client/cmd/pcloud/main.go
index fa013c9..1440af6 100644
--- a/core/client/cmd/pcloud/main.go
+++ b/core/client/cmd/pcloud/main.go
@@ -26,6 +26,7 @@
 type processor struct {
 	vc  VPNClient
 	app App
+	st  Storage
 	ui  *UI
 
 	inviteQrCh        chan image.Image
@@ -33,18 +34,23 @@
 
 	onConnectCh    chan interface{}
 	onDisconnectCh chan interface{}
+
+	onConfigCh chan struct{}
 }
 
 func newProcessor() *processor {
 	th := material.NewTheme(gofont.Collection())
+	app := createApp()
 	return &processor{
 		vc:                NewDirectVPNClient(*vpnApiAddr),
-		app:               createApp(),
+		app:               app,
+		st:                app.CreateStorage(),
 		ui:                NewUI(th),
 		inviteQrCh:        make(chan image.Image, 1),
 		inviteQrScannedCh: make(chan []byte, 1),
 		onConnectCh:       make(chan interface{}, 1),
 		onDisconnectCh:    make(chan interface{}, 1),
+		onConfigCh:        make(chan struct{}, 1),
 	}
 }
 
@@ -79,6 +85,13 @@
 				} else {
 					w.Invalidate()
 				}
+				if config, err := p.st.Get(); err != nil {
+					return err
+				} else {
+					if config.Network != nil {
+						p.onConfigCh <- struct{}{}
+					}
+				}
 			case *system.CommandEvent:
 				if e.Type == system.CommandBack {
 					if p.ui.OnBack() {
@@ -98,11 +111,24 @@
 			p.ui.InviteQRGenerated(img)
 			w.Invalidate()
 		case code := <-p.inviteQrScannedCh:
-			p.JoinNetworkAndConnect(code)
-		case s := <-p.onConnectCh:
-			if err := p.app.Connect(s); err != nil {
+			if err := p.JoinAndGetNetworkConfig(code); err != nil {
 				return err
 			}
+		case <-p.onConfigCh:
+			if err := p.app.TriggerService(); err != nil {
+				return err
+			}
+		case s := <-p.onConnectCh:
+			if err := p.app.UpdateService(s); err != nil {
+				return err
+			}
+			if config, err := p.st.Get(); err != nil {
+				return err
+			} else {
+				if err := p.app.Connect(config); err != nil {
+					return err
+				}
+			}
 		}
 	}
 	return nil
@@ -136,13 +162,17 @@
 }
 
 func (p *processor) generateInviteQRCode() (image.Image, error) {
+	config, err := p.st.Get()
+	if err != nil {
+		return nil, err
+	}
 	message := []byte("Hello PCloud")
-	signature, err := p.vc.Sign(message)
+	signature, err := p.vc.Sign(config.ApiAddr, message)
 	if err != nil {
 		return nil, err
 	}
 	c := qrCodeData{
-		p.vc.Address(),
+		config.ApiAddr,
 		message,
 		signature,
 	}
@@ -161,18 +191,20 @@
 	return img, nil
 }
 
-func (p *processor) JoinNetworkAndConnect(code []byte) {
+func (p *processor) JoinAndGetNetworkConfig(code []byte) error {
 	var invite qrCodeData
 	if err := json.NewDecoder(bytes.NewReader(code)).Decode(&invite); err != nil {
 		panic(err)
 	}
-	config, err := p.vc.Join(invite.VPNApiAddr, invite.Message, invite.Signature)
+	network, err := p.vc.Join(invite.VPNApiAddr, invite.Message, invite.Signature)
 	if err != nil {
-		panic(err)
+		return err
 	}
-	if err := p.app.StartVPN(config); err != nil {
-		panic(err)
+	if err := p.st.Store(Config{invite.VPNApiAddr, network}); err != nil {
+		return err
 	}
+	p.onConfigCh <- struct{}{}
+	return nil
 }
 
 func main() {
@@ -185,11 +217,3 @@
 	}()
 	app.Main()
 }
-
-// fmt.Println(m["pki"])
-// c := nc.NewC(logrus.StandardLogger())
-// if err := c.LoadString(string(tmpl)); err != nil {
-// 	return nil, err
-// }
-// fmt.Println(c.Settings["pki"])
-// return c, nil
diff --git a/core/client/cmd/pcloud/storage.go b/core/client/cmd/pcloud/storage.go
new file mode 100644
index 0000000..824cc84
--- /dev/null
+++ b/core/client/cmd/pcloud/storage.go
@@ -0,0 +1,6 @@
+package main
+
+type Storage interface {
+	Get() (Config, error)
+	Store(Config) error
+}
diff --git a/core/client/cmd/pcloud/storage_android.go b/core/client/cmd/pcloud/storage_android.go
new file mode 100644
index 0000000..6e787da
--- /dev/null
+++ b/core/client/cmd/pcloud/storage_android.go
@@ -0,0 +1,71 @@
+package main
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+
+	"github.com/giolekva/pcloud/core/client/jni"
+)
+
+type androidStorage struct {
+	jvm     *jni.JVM
+	appCtx  jni.Object
+	encrypt jni.MethodID
+	decrypt jni.MethodID
+}
+
+func CreateStorage(jvm *jni.JVM, appCtx jni.Object) Storage {
+	s := &androidStorage{jvm: jvm, appCtx: appCtx}
+	jni.Do(jvm, func(env *jni.Env) error {
+		appCls := jni.GetObjectClass(env, appCtx)
+		s.encrypt = jni.GetMethodID(
+			env, appCls,
+			"encryptToPref", "(Ljava/lang/String;Ljava/lang/String;)V",
+		)
+		s.decrypt = jni.GetMethodID(
+			env, appCls,
+			"decryptFromPref", "(Ljava/lang/String;)Ljava/lang/String;",
+		)
+		return nil
+	})
+	return s
+}
+
+func (s *androidStorage) Get() (Config, error) {
+	var data []byte
+	err := jni.Do(s.jvm, func(env *jni.Env) error {
+		jfile := jni.JavaString(env, "config")
+		plain, err := jni.CallObjectMethod(env, s.appCtx, s.decrypt,
+			jni.Value(jfile))
+		if err != nil {
+			panic(err)
+			return err
+		}
+		b64 := jni.GoString(env, jni.String(plain))
+		if b64 == "" {
+			return nil
+		}
+		data, err = base64.RawStdEncoding.DecodeString(b64)
+		return err
+	})
+	var config Config
+	if data != nil {
+		err = json.NewDecoder(bytes.NewReader(data)).Decode(&config)
+	}
+	return config, err
+}
+
+func (s *androidStorage) Store(config Config) error {
+	var data bytes.Buffer
+	if err := json.NewEncoder(&data).Encode(config); err != nil {
+		return err
+	}
+	bs64 := base64.RawStdEncoding.EncodeToString(data.Bytes())
+	return jni.Do(s.jvm, func(env *jni.Env) error {
+		jfile := jni.JavaString(env, "config")
+		jplain := jni.JavaString(env, bs64)
+		return jni.CallVoidMethod(env, s.appCtx, s.encrypt,
+			jni.Value(jfile), jni.Value(jplain))
+	})
+}
diff --git a/core/client/cmd/pcloud/storage_darwin.go b/core/client/cmd/pcloud/storage_darwin.go
new file mode 100644
index 0000000..8fabc98
--- /dev/null
+++ b/core/client/cmd/pcloud/storage_darwin.go
@@ -0,0 +1,18 @@
+package main
+
+import "errors"
+
+type darwinStorage struct {
+}
+
+func CreateStorage() Storage {
+	return &darwinStorage{}
+}
+
+func (s *darwinStorage) Get() (Config, error) {
+	return nil, errors.New("Not implemented")
+}
+
+func (s *darwinStorage) Store(config Config) error {
+	return errors.New("Not implemented")
+}
diff --git a/core/client/cmd/pcloud/ui.go b/core/client/cmd/pcloud/ui.go
index 6f805fa..78afa76 100644
--- a/core/client/cmd/pcloud/ui.go
+++ b/core/client/cmd/pcloud/ui.go
@@ -96,12 +96,16 @@
 }
 
 func (ui *UI) layoutActions(gtx C) D {
-	return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
+	return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceAround, WeightSum: 2.2}.Layout(gtx,
 		layout.Flexed(1, func(gtx C) D {
-			return material.Button(ui.th, &ui.invite.open, "Invite").Layout(gtx)
+			b := material.Button(ui.th, &ui.invite.open, "Invite")
+			b.CornerRadius = unit.Px(20)
+			return b.Layout(gtx)
 		}),
 		layout.Flexed(1, func(gtx C) D {
-			return material.Button(ui.th, &ui.join.open, "Join").Layout(gtx)
+			b := material.Button(ui.th, &ui.join.open, "Join")
+			b.CornerRadius = unit.Px(20)
+			return b.Layout(gtx)
 		}),
 	)
 }