Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package qubesome for openSUSE:Factory 
checked in at 2024-12-09 21:13:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/qubesome (Old)
 and      /work/SRC/openSUSE:Factory/.qubesome.new.29675 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "qubesome"

Mon Dec  9 21:13:03 2024 rev:3 rq:1229429 version:0.0.8

Changes:
--------
--- /work/SRC/openSUSE:Factory/qubesome/qubesome.changes        2024-11-26 
20:56:10.479121091 +0100
+++ /work/SRC/openSUSE:Factory/.qubesome.new.29675/qubesome.changes     
2024-12-09 21:14:06.531335200 +0100
@@ -1,0 +2,26 @@
+Sun Dec 08 09:47:22 UTC 2024 - [email protected]
+
+- Update to version 0.0.8:
+  * obs: Remove rebuild process
+  * build: Group dependabot updates
+  * profiles: Check whether profile is running Instead of erroring, confirm 
whether the profile is running, if it isn't it is safe to delete to file and 
start a new profile.
+  * build(deps): bump github.com/urfave/cli/v3
+  * Add GPU support for podman
+  * Move drive and env pkgs to internal/util
+  * obs: Add rebuild_package step on push
+  * Move ephemeral dir to ~/.qubesome/run Previously the ephemeral dir for 
qubesome was kept at /run/user/1000/qubesome, this has now moved to 
~/.qubesome/run instead. Profile dirs are created inside the new dir and 
removed once 'qubesome start' finishes.
+  * build: Add fuzz testing checks
+  * profile: Enforce mTLS with inception server
+  * obs: Move source project to :unstable A new long standing project will be 
the home of qubesome packages when changes are merged into main. The new 
project name is: home:pjbgf:devel:languages:go:unstable
+  * build(deps): bump github.com/urfave/cli/v3
+  * obs: Trigger services on PR merge
+  * WSL: Eval symlink to .X11-unix dir
+  * xauth: Decrease min magic cookie length to 40 This is causing issues on 
Tumbleweed distrobox in WSL, where the cookie being generated is 49 chars long.
+  * Remove redundant files
+  * xauth: Add Fuzz tests
+  * clip: Show active profiles for autocomplete
+  * build: Pin GH Actions. Set bump interval to monthly
+  * obs: Change target project to align with branch convention
+  * build: Add OBS integration
+
+-------------------------------------------------------------------

Old:
----
  qubesome-0.0.7.tar.gz

New:
----
  qubesome-0.0.8.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ qubesome.spec ++++++
--- /var/tmp/diff_new_pack.yG4Wjd/_old  2024-12-09 21:14:07.019355618 +0100
+++ /var/tmp/diff_new_pack.yG4Wjd/_new  2024-12-09 21:14:07.023355785 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           qubesome
-Version:        0.0.7
+Version:        0.0.8
 Release:        0
 Summary:        Containerize Window Managers, apps and config from a 
declarative state in Git
 License:        Apache-2.0
@@ -41,7 +41,7 @@
 
 %check
 # execute the binary as a basic check
-./%{name} deps show
+./%{name} deps
 
 %install
 install -D -m 0755 %{name} "%{buildroot}/%{_bindir}/%{name}"

++++++ _service ++++++
--- /var/tmp/diff_new_pack.yG4Wjd/_old  2024-12-09 21:14:07.051356957 +0100
+++ /var/tmp/diff_new_pack.yG4Wjd/_new  2024-12-09 21:14:07.055357124 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/qubesome/cli.git</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v0.0.7</param>
+    <param name="revision">v0.0.8</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">enable</param>
     <param name="match-tag">v*</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.yG4Wjd/_old  2024-12-09 21:14:07.075357961 +0100
+++ /var/tmp/diff_new_pack.yG4Wjd/_new  2024-12-09 21:14:07.075357961 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/qubesome/cli.git</param>
-              <param 
name="changesrevision">698eb74437070f2fe7c9623ad1702369f2b6e5b6</param></service></servicedata>
+              <param 
name="changesrevision">f21dcef5cda98bca020f8463171184c991efd59a</param></service></servicedata>
 (No newline at EOF)
 

++++++ qubesome-0.0.7.tar.gz -> qubesome-0.0.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/.github/dependabot.yaml 
new/qubesome-0.0.8/.github/dependabot.yaml
--- old/qubesome-0.0.7/.github/dependabot.yaml  2024-11-25 11:55:53.000000000 
+0100
+++ new/qubesome-0.0.8/.github/dependabot.yaml  2024-12-08 10:40:21.000000000 
+0100
@@ -4,8 +4,16 @@
     directory: "/"
     schedule:
       interval: "weekly"
+    groups:
+      golang.org:
+        patterns:
+          - "golang.org/*"
 
   - package-ecosystem: "github-actions"
     directory: "/"
     schedule:
-      interval: "weekly"
+      interval: "monthly"
+    groups:
+      github-actions-updates:
+        patterns:
+          - "*"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/.github/workflows/fuzz.yml 
new/qubesome-0.0.8/.github/workflows/fuzz.yml
--- old/qubesome-0.0.7/.github/workflows/fuzz.yml       1970-01-01 
01:00:00.000000000 +0100
+++ new/qubesome-0.0.8/.github/workflows/fuzz.yml       2024-12-08 
10:40:21.000000000 +0100
@@ -0,0 +1,42 @@
+name: fuzz tests
+
+on:
+  push:
+  workflow_dispatch:
+
+  schedule:
+    - cron: "0 7 * * 6"
+
+permissions: {}
+
+jobs:
+  fuzz:
+    runs-on: ubuntu-latest
+
+    permissions:
+      contents: read
+      actions: read # for cache access
+    env:
+      FUZZ_TIME: 10m
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@cbb722410c2e876e24abbe8de2cc27693e501dcb # 
v4.2.2
+
+      - name: Set up Go
+        uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # 
v5.1.0
+        with:
+          go-version: stable
+
+      - name: Set environment variable based on trigger
+        run: |
+          if [[ "${{ github.event_name }}" != "push" ]]; then
+            echo "FUZZ_TIME=30m" >> $GITHUB_ENV
+          fi
+
+      - name: Fuzzing
+        uses: 
form3tech-oss/go-ci-fuzz/ci/github-actions/fuzz@4663eaaadb263d2621592c62681dac7f7002d582
+        with:
+          fuzz-time: ${{ env.FUZZ_TIME }}
+          fail-fast: true
+          version: 0.1.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/.github/workflows/release.yml 
new/qubesome-0.0.8/.github/workflows/release.yml
--- old/qubesome-0.0.7/.github/workflows/release.yml    2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/.github/workflows/release.yml    2024-12-08 
10:40:21.000000000 +0100
@@ -18,20 +18,20 @@
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@cbb722410c2e876e24abbe8de2cc27693e501dcb # 
v4.2.2
         with:
           fetch-depth: 0
 
       - name: Set up Go
-        uses: actions/setup-go@v5
+        uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # 
v5.1.0
         with:
           go-version: stable
 
-      - uses: anchore/sbom-action/[email protected]
-      - uses: sigstore/[email protected]
+      - uses: 
anchore/sbom-action/download-syft@55dc4ee22412511ee8c3142cbea40418e6cec693 # 
v0.17.8
+      - uses: 
sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
 
       - name: Run GoReleaser
-        uses: goreleaser/goreleaser-action@v6
+        uses: 
goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
         with:
           distribution: goreleaser
           version: '~> v2'
@@ -40,6 +40,6 @@
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Attest release artefacts
-        uses: actions/attest-build-provenance@v1
+        uses: 
actions/attest-build-provenance@ef244123eb79f2f7a7e75d99086184180e6d0018 # 
v1.4.4
         with:
           subject-path: "dist/qubesome*"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/.github/workflows/test.yml 
new/qubesome-0.0.8/.github/workflows/test.yml
--- old/qubesome-0.0.7/.github/workflows/test.yml       2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/.github/workflows/test.yml       2024-12-08 
10:40:21.000000000 +0100
@@ -14,10 +14,10 @@
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@cbb722410c2e876e24abbe8de2cc27693e501dcb # 
v4.2.2
 
       - name: Set up Go
-        uses: actions/setup-go@v5
+        uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # 
v5.1.0
         with:
           go-version: stable
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/.obs/workflows.yml 
new/qubesome-0.0.8/.obs/workflows.yml
--- old/qubesome-0.0.7/.obs/workflows.yml       1970-01-01 01:00:00.000000000 
+0100
+++ new/qubesome-0.0.8/.obs/workflows.yml       2024-12-08 10:40:21.000000000 
+0100
@@ -0,0 +1,20 @@
+version: '1.1'
+testbuild:
+  steps:
+    - branch_package:
+        source_project: home:pjbgf:devel:languages:go:unstable
+        source_package: qubesome
+        target_project: home:pjbgf:ci
+        add_repositories: disable
+    - configure_repositories:
+        project: home:pjbgf:ci
+        repositories:
+          - name: openSUSE_Factory
+            paths:
+              - target_project: home:pjbgf:devel:languages:go:unstable
+                target_repository: openSUSE_Factory
+            architectures:
+              - x86_64
+              - aarch64
+  filters:
+    event: pull_request
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/cmd/cli/clipboard.go 
new/qubesome-0.0.8/cmd/cli/clipboard.go
--- old/qubesome-0.0.7/cmd/cli/clipboard.go     2024-11-25 11:55:53.000000000 
+0100
+++ new/qubesome-0.0.8/cmd/cli/clipboard.go     2024-12-08 10:40:21.000000000 
+0100
@@ -48,6 +48,14 @@
                                Flags: []cli.Flag{
                                        clipType,
                                },
+                               ShellComplete: func(ctx context.Context, cmd 
*cli.Command) {
+                                       if cmd.NArg() > 0 {
+                                               return
+                                       }
+                                       for _, p := range activeProfiles() {
+                                               fmt.Println(p)
+                                       }
+                               },
                                Action: func(ctx context.Context, c 
*cli.Command) error {
                                        target, err := 
profileOrActive(targetProfile)
                                        if err != nil {
@@ -88,6 +96,14 @@
                                Flags: []cli.Flag{
                                        clipType,
                                },
+                               ShellComplete: func(ctx context.Context, cmd 
*cli.Command) {
+                                       if cmd.NArg() > 1 {
+                                               return
+                                       }
+                                       for _, p := range activeProfiles() {
+                                               fmt.Println(p)
+                                       }
+                               },
                                Action: func(ctx context.Context, c 
*cli.Command) error {
                                        cfg := 
profileConfigOrDefault(targetProfile)
 
@@ -136,6 +152,14 @@
                                Flags: []cli.Flag{
                                        clipType,
                                },
+                               ShellComplete: func(ctx context.Context, cmd 
*cli.Command) {
+                                       if cmd.NArg() > 0 {
+                                               return
+                                       }
+                                       for _, p := range activeProfiles() {
+                                               fmt.Println(p)
+                                       }
+                               },
                                Action: func(ctx context.Context, c 
*cli.Command) error {
                                        target, err := 
profileOrActive(sourceProfile)
                                        if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/cmd/cli/root.go 
new/qubesome-0.0.8/cmd/cli/root.go
--- old/qubesome-0.0.7/cmd/cli/root.go  2024-11-25 11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/cmd/cli/root.go  2024-12-08 10:40:21.000000000 +0100
@@ -157,3 +157,14 @@
 
        return active
 }
+
+func activeProfiles() []string {
+       cfgs := activeConfigs()
+       profiles := make([]string, 0, len(cfgs))
+
+       for _, file := range cfgs {
+               profiles = append(profiles, 
strings.TrimSuffix(filepath.Base(file), ".config"))
+       }
+
+       return profiles
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/go.mod new/qubesome-0.0.8/go.mod
--- old/qubesome-0.0.7/go.mod   2024-11-25 11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/go.mod   2024-12-08 10:40:21.000000000 +0100
@@ -7,7 +7,7 @@
        github.com/go-git/go-git/v5 v5.12.1-0.20241115094014-70dd9f8347eb
        github.com/google/uuid v1.6.0
        github.com/stretchr/testify v1.10.0
-       github.com/urfave/cli/v3 v3.0.0-alpha9.5
+       github.com/urfave/cli/v3 v3.0.0-beta1
        golang.org/x/sys v0.27.0
        google.golang.org/grpc v1.68.0
        google.golang.org/protobuf v1.35.2
@@ -30,7 +30,6 @@
        github.com/pmezard/go-difflib v1.0.0 // indirect
        github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // 
indirect
        github.com/skeema/knownhosts v1.3.0 // indirect
-       github.com/stretchr/objx v0.5.2 // indirect
        github.com/xanzy/ssh-agent v0.3.3 // indirect
        golang.org/x/crypto v0.29.0 // indirect
        golang.org/x/net v0.31.0 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/go.sum new/qubesome-0.0.8/go.sum
--- old/qubesome-0.0.7/go.sum   2024-11-25 11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/go.sum   2024-12-08 10:40:21.000000000 +0100
@@ -65,14 +65,12 @@
 github.com/skeema/knownhosts v1.3.0 
h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
 github.com/skeema/knownhosts v1.3.0/go.mod 
h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
-github.com/stretchr/objx v0.5.2/go.mod 
h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.2.2/go.mod 
h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.4.0/go.mod 
h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/urfave/cli/v3 v3.0.0-alpha9.5 
h1:Y693/B4H1nO6s5Xxns2AVLWNZ650HCF7AjThgvkP0bw=
-github.com/urfave/cli/v3 v3.0.0-alpha9.5/go.mod 
h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
+github.com/urfave/cli/v3 v3.0.0-beta1 
h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg=
+github.com/urfave/cli/v3 v3.0.0-beta1/go.mod 
h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
 github.com/xanzy/ssh-agent v0.3.3 
h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
 github.com/xanzy/ssh-agent v0.3.3/go.mod 
h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod 
h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/command/mocks.go 
new/qubesome-0.0.8/internal/command/mocks.go
--- old/qubesome-0.0.7/internal/command/mocks.go        2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/command/mocks.go        1970-01-01 
01:00:00.000000000 +0100
@@ -1,95 +0,0 @@
-package command
-
-import (
-       "github.com/qubesome/cli/internal/types"
-       "github.com/stretchr/testify/mock"
-)
-
-func NewHandlerMock[T any](f func(a App) (Action[T], []Option[T], error)) 
*HandlerMock[T] {
-       return &HandlerMock[T]{
-               handle: f,
-       }
-}
-
-type HandlerMock[T any] struct {
-       handle func(a App) (Action[T], []Option[T], error)
-       mock.Mock
-}
-
-func (m *HandlerMock[T]) Handle(a App) (Action[T], []Option[T], error) {
-       return m.handle(a)
-}
-
-func (m *HandlerMock[T]) Run(opts ...Option[T]) error {
-       args := m.Called(opts)
-       return args.Error(0)
-}
-
-type ConsoleMock[T any] struct {
-       mock.Mock
-}
-
-func (m *ConsoleMock[T]) ExecName() string {
-       args := m.Called()
-       return args.String(0)
-}
-
-func (m *ConsoleMock[T]) Args() []string {
-       args := m.Called()
-       return args.Get(0).([]string) //nolint
-}
-
-func (m *ConsoleMock[T]) UserConfig() *types.Config {
-       args := m.Called()
-       cfg := args.Get(0)
-
-       if cfg == nil {
-               return nil
-       }
-
-       return cfg.(*types.Config) //nolint
-}
-
-func (m *ConsoleMock[T]) ProfileConfig(a string) *types.Config {
-       args := m.Called(a)
-       cfg := args.Get(0)
-
-       if cfg == nil {
-               return nil
-       }
-
-       return cfg.(*types.Config) //nolint
-}
-
-func (m *ConsoleMock[T]) Command(name string) bool {
-       args := m.Called(name)
-       return args.Bool(0)
-}
-
-func (m *ConsoleMock[T]) RunSubCommand() error {
-       args := m.Called()
-       return args.Error(0)
-}
-
-func (m *ConsoleMock[T]) Usage(format string) {
-       m.Called(format)
-}
-
-func (m *ConsoleMock[T]) Exit(code int) {
-       m.Called(code)
-}
-
-func (m *ConsoleMock[T]) Printf(format string, a ...any) (int, error) {
-       args := m.Called(format, a)
-       return args.Int(0), args.Error(1)
-}
-
-func (m *ConsoleMock[T]) Handle(a App) (Action[T], []Option[T], error) {
-       args := m.Called(a)
-       return args.Get(0).(Action[T]), args.Get(1).([]Option[T]), 
args.Error(2) //nolint
-}
-
-func (m *ConsoleMock[T]) Run(opts ...Option[T]) error {
-       args := m.Called(opts)
-       return args.Error(0)
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/drive/drive.go 
new/qubesome-0.0.8/internal/drive/drive.go
--- old/qubesome-0.0.7/internal/drive/drive.go  2024-11-25 11:55:53.000000000 
+0100
+++ new/qubesome-0.0.8/internal/drive/drive.go  1970-01-01 01:00:00.000000000 
+0100
@@ -1,47 +0,0 @@
-package drive
-
-import (
-       "bufio"
-       "bytes"
-       "fmt"
-       "os"
-       "strings"
-)
-
-const mountsFile = "/proc/mounts"
-
-var readFile = os.ReadFile
-
-func Mounts(drive, mount string) (bool, error) {
-       if drive == "" {
-               return false, fmt.Errorf("drive is empty")
-       }
-       if mount == "" {
-               return false, fmt.Errorf("mount is empty")
-       }
-
-       data, err := readFile(mountsFile)
-       if err != nil {
-               return false, fmt.Errorf("failed to open mounts file: %w", err)
-       }
-
-       r := bytes.NewReader(data)
-       scanner := bufio.NewScanner(r)
-
-       for scanner.Scan() {
-               fields := strings.Fields(scanner.Text())
-               if fields[0] != drive {
-                       continue
-               }
-
-               if len(fields) <= 1 {
-                       continue
-               }
-
-               if fields[1] == mount {
-                       return true, nil
-               }
-       }
-
-       return false, nil
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/drive/drive_test.go 
new/qubesome-0.0.8/internal/drive/drive_test.go
--- old/qubesome-0.0.7/internal/drive/drive_test.go     2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/drive/drive_test.go     1970-01-01 
01:00:00.000000000 +0100
@@ -1,89 +0,0 @@
-package drive
-
-import (
-       "os"
-       "testing"
-
-       "github.com/stretchr/testify/assert"
-       "github.com/stretchr/testify/require"
-)
-
-func TestMounts(t *testing.T) {
-       tests := []struct {
-               name         string
-               drive        string
-               mount        string
-               want         bool
-               readFileFunc func(name string) ([]byte, error)
-               err          string
-       }{
-               {
-                       name:  "empty drive",
-                       mount: "/media",
-                       err:   "drive is empty",
-               },
-               {
-                       name:  "empty mount",
-                       drive: "/media",
-                       err:   "mount is empty",
-               },
-               {
-                       name:  "drive not found",
-                       drive: "foo-bar",
-                       mount: "/media",
-                       readFileFunc: func(name string) ([]byte, error) {
-                               return []byte("foo\nbar\n"), nil
-                       },
-               },
-               {
-                       name:  "mounts file not found",
-                       drive: "foo-bar",
-                       mount: "/media",
-                       readFileFunc: func(name string) ([]byte, error) {
-                               return nil, os.ErrNotExist
-                       },
-                       err: "failed to open mounts file: file does not exist",
-               },
-               {
-                       name:  "single mount point",
-                       drive: "foo-bar",
-                       mount: "/media/foo/bar",
-                       readFileFunc: func(name string) ([]byte, error) {
-                               return []byte("foo\nbar\nfoo-bar 
/media/foo/bar\n"), nil
-                       },
-                       want: true,
-               },
-               {
-                       name:  "multiple mount points",
-                       drive: "foo-bar",
-                       mount: "/media/foo/bar",
-                       readFileFunc: func(name string) ([]byte, error) {
-                               return []byte("foo-bar 
/foo/for/bar\nfoo\nbar\nfoo-bar /media/foo/bar\n"), nil
-                       },
-                       want: true,
-               },
-               {
-                       name:  "not right mount point",
-                       drive: "foo-bar",
-                       mount: "/media/bar/foo",
-                       readFileFunc: func(name string) ([]byte, error) {
-                               return []byte("foo\nbar\nfoo-bar 
/media/foo/bar\n"), nil
-                       },
-                       want: false,
-               },
-       }
-
-       for _, tc := range tests {
-               t.Run(tc.name, func(t *testing.T) {
-                       readFile = tc.readFileFunc
-                       got, err := Mounts(tc.drive, tc.mount)
-
-                       if tc.err == "" {
-                               require.NoError(t, err)
-                               assert.Equal(t, tc.want, got)
-                       } else {
-                               require.ErrorContains(t, err, tc.err)
-                       }
-               })
-       }
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/env/env.go 
new/qubesome-0.0.8/internal/env/env.go
--- old/qubesome-0.0.7/internal/env/env.go      2024-11-25 11:55:53.000000000 
+0100
+++ new/qubesome-0.0.8/internal/env/env.go      1970-01-01 01:00:00.000000000 
+0100
@@ -1,41 +0,0 @@
-package env
-
-import (
-       "fmt"
-       "log/slog"
-       "os"
-)
-
-func init() { //nolint
-       h, _ := os.UserHomeDir()
-       _ = Update("HOME", h)
-}
-
-var mapping = map[string]string{
-       "HOME":   "",
-       "GITDIR": "",
-}
-
-func Update(k, v string) error {
-       slog.Debug("setting env", k, v)
-       if _, ok := mapping[k]; ok {
-               mapping[k] = v
-               return nil
-       }
-       return fmt.Errorf("%q is not an expandable env var", k)
-}
-
-func Add(k, v string) {
-       mapping[k] = v
-}
-
-func Expand(in string) string {
-       return os.Expand(in, expand)
-}
-
-func expand(s string) string {
-       if out, ok := mapping[s]; ok {
-               return out
-       }
-       return ""
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/files/files.go 
new/qubesome-0.0.8/internal/files/files.go
--- old/qubesome-0.0.7/internal/files/files.go  2024-11-25 11:55:53.000000000 
+0100
+++ new/qubesome-0.0.8/internal/files/files.go  2024-12-08 10:40:21.000000000 
+0100
@@ -3,8 +3,8 @@
 // Key locations:
 // - ~/.qubesome: default location for persistent files.
 // - ~/.qubesome/images-last-checked: file that stores when images were last 
checked.
-// - /run/user/%d/qubesome: root of ephemeral files.
-// - /run/user/%d/qubesome/git/<git-url>/<path>: where git repositories
+// - ~/.qubesome/run: root of ephemeral files.
+// - ~/.qubesome/git/<git-url>/<path>: where git repositories
 // are cloned to.
 package files
 
@@ -53,7 +53,7 @@
 
 // RunUserQubesome returns the path to the user-specific qubesome directory.
 func RunUserQubesome() string {
-       return fmt.Sprintf("/run/user/%d/qubesome", os.Getuid())
+       return filepath.Join(QubesomeDir(), "run")
 }
 
 // ClientCookiePath returns the path to the client cookie file for the given 
profile.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/inception/client.go 
new/qubesome-0.0.8/internal/inception/client.go
--- old/qubesome-0.0.7/internal/inception/client.go     2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/inception/client.go     2024-12-08 
10:40:21.000000000 +0100
@@ -2,14 +2,18 @@
 
 import (
        "context"
+       "crypto/tls"
+       "crypto/x509"
        "fmt"
        "log/slog"
+       "os"
        "strings"
        "time"
 
+       "github.com/qubesome/cli/internal/util/mtls"
        pb "github.com/qubesome/cli/pkg/inception/proto"
        "google.golang.org/grpc"
-       "google.golang.org/grpc/credentials/insecure"
+       "google.golang.org/grpc/credentials"
 )
 
 func NewClient(socket string) *Client {
@@ -22,8 +26,42 @@
        socket string
 }
 
+func getCreds() (credentials.TransportCredentials, error) {
+       caPEM := []byte(os.Getenv("Q_MTLS_CA"))
+       certPEM := []byte(os.Getenv("Q_MTLS_CERT"))
+       keyPEM := []byte(os.Getenv("Q_MTLS_KEY"))
+
+       cert, err := tls.X509KeyPair(certPEM, keyPEM)
+       if err != nil {
+               return nil, err
+       }
+
+       certPool := x509.NewCertPool()
+       if !certPool.AppendCertsFromPEM(caPEM) {
+               return nil, err
+       }
+
+       creds := credentials.NewTLS(&tls.Config{
+               Certificates: []tls.Certificate{cert},
+               RootCAs:      certPool,
+               MinVersion:   tls.VersionTLS13,
+               // The connection is made via unix socket, so generally the
+               // expected server name will be localhost - unless overridden
+               // by ServerName.
+               ServerName: mtls.HostServerName,
+       })
+
+       return creds, nil
+}
+
 func (c *Client) XdgOpen(ctx context.Context, url string) error {
-       conn, err := grpc.NewClient(c.socket, 
grpc.WithTransportCredentials(insecure.NewCredentials()))
+       creds, err := getCreds()
+       if err != nil {
+               return err
+       }
+
+       conn, err := grpc.NewClient(c.socket,
+               grpc.WithTransportCredentials(creds))
        if err != nil {
                return fmt.Errorf("failed to connect to qubesome host: %w", err)
        }
@@ -44,7 +82,12 @@
 }
 
 func (c *Client) Run(ctx context.Context, workload string, args []string) 
error {
-       conn, err := grpc.NewClient(c.socket, 
grpc.WithTransportCredentials(insecure.NewCredentials()))
+       creds, err := getCreds()
+       if err != nil {
+               return err
+       }
+
+       conn, err := grpc.NewClient(c.socket, 
grpc.WithTransportCredentials(creds))
        if err != nil {
                return fmt.Errorf("failed to connect to qubesome host: %w", err)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/profiles/profiles.go 
new/qubesome-0.0.8/internal/profiles/profiles.go
--- old/qubesome-0.0.7/internal/profiles/profiles.go    2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/profiles/profiles.go    2024-12-08 
10:40:21.000000000 +0100
@@ -16,14 +16,15 @@
        "github.com/go-git/go-git/v5/plumbing/transport/ssh"
        "github.com/google/uuid"
        "github.com/qubesome/cli/internal/command"
-       "github.com/qubesome/cli/internal/drive"
-       "github.com/qubesome/cli/internal/env"
        "github.com/qubesome/cli/internal/files"
        "github.com/qubesome/cli/internal/images"
        "github.com/qubesome/cli/internal/runners/util/container"
        "github.com/qubesome/cli/internal/types"
        "github.com/qubesome/cli/internal/util/dbus"
+       "github.com/qubesome/cli/internal/util/drive"
+       "github.com/qubesome/cli/internal/util/env"
        "github.com/qubesome/cli/internal/util/gpu"
+       "github.com/qubesome/cli/internal/util/mtls"
        "github.com/qubesome/cli/internal/util/resolution"
        "github.com/qubesome/cli/internal/util/xauth"
        "github.com/qubesome/cli/pkg/inception"
@@ -85,10 +86,10 @@
        ln := files.ProfileConfig(name)
 
        if _, err := os.Lstat(ln); err == nil {
-               // Wayland is not cleaning up profile state after closure.
-               if !strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") 
{
+               if container.Running(runner, fmt.Sprintf(ContainerNameFormat, 
name)) {
                        return fmt.Errorf("profile %q is already started", name)
                }
+
                if err = os.Remove(ln); err != nil {
                        return fmt.Errorf("failed to remove leftover profile 
symlink: %w", err)
                }
@@ -167,10 +168,6 @@
                return fmt.Errorf("cannot file profile %q in config %q", name, 
cfgPath)
        }
 
-       if p.Runner != "" {
-               runner = p.Runner
-       }
-
        // When sourcing from git, ensure profile path is relative to the git 
repository.
        pp, err := securejoin.SecureJoin(filepath.Dir(cfgPath), p.Path)
        if err != nil {
@@ -197,6 +194,12 @@
                return err
        }
 
+       // If runner is not being overwritten (via -runner), use the runner
+       // set at profile level in the config.
+       if runner == "" && profile.Runner != "" {
+               runner = profile.Runner
+       }
+
        binary := files.ContainerRunnerBinary(runner)
        fi, err := os.Lstat(binary)
        if err != nil || !fi.Mode().IsRegular() {
@@ -250,11 +253,15 @@
                return err
        }
 
+       creds, err := mtls.NewCredentials()
+       if err != nil {
+               return err
+       }
        go func() {
                defer wg.Done()
 
                server := inception.NewServer(profile, cfg)
-               err1 := server.Listen(sockPath)
+               err1 := server.Listen(creds.ServerCert, creds.CA, sockPath)
                if err1 != nil {
                        slog.Debug("error listening to socket", "error", err1)
                        if err == nil {
@@ -264,7 +271,12 @@
        }()
 
        defer func() {
-               _ = os.Remove(sockPath)
+               // Clean up the profile dir once profile finishes.
+               pd := files.ProfileDir(profile.Name)
+               err = os.RemoveAll(pd)
+               if err != nil {
+                       slog.Warn("failed to remove profile dir", "path", pd, 
"error", err)
+               }
        }()
 
        err = createMagicCookie(profile)
@@ -272,7 +284,9 @@
                return err
        }
 
-       err = createNewDisplay(binary, profile, 
strconv.Itoa(int(profile.Display)))
+       err = createNewDisplay(binary,
+               creds.CA, creds.ClientPEM, creds.ClientKeyPEM,
+               profile, strconv.Itoa(int(profile.Display)))
        if err != nil {
                return err
        }
@@ -356,7 +370,7 @@
        return nil
 }
 
-func createNewDisplay(bin string, profile *types.Profile, display string) 
error {
+func createNewDisplay(bin string, ca, cert, key []byte, profile 
*types.Profile, display string) error {
        command := "Xephyr"
        res, err := resolution.Primary()
        if err != nil {
@@ -433,10 +447,20 @@
                break
        }
 
+       x11Dir := "/tmp/.X11-unix"
+       if os.Getenv("WSL_DISTRO_NAME") != "" {
+               fmt.Println("\033[33mWARN: Running qubesome in WSL is 
experimental. Some features may not work as expected.\033[0m")
+               fp, err := filepath.EvalSymlinks(x11Dir)
+               if err != nil {
+                       return fmt.Errorf("failed to eval symlink: %w", err)
+               }
+               x11Dir = fp
+       }
+
        //nolint
        var paths []string
        paths = append(paths, "-v=/etc/localtime:/etc/localtime:ro")
-       paths = append(paths, "-v=/tmp/.X11-unix:/tmp/.X11-unix:rw")
+       paths = append(paths, fmt.Sprintf("-v=%s:/tmp/.X11-unix:rw", x11Dir))
        paths = append(paths, fmt.Sprintf("-v=%s:/tmp/qube.sock:ro", socket))
        paths = append(paths, fmt.Sprintf("-v=%s:/home/xorg-user/.Xserver", 
server))
        paths = append(paths, fmt.Sprintf("-v=%s:/home/xorg-user/.Xauthority", 
workload))
@@ -453,6 +477,9 @@
                // rely on currently set DISPLAY.
                "-e", "DISPLAY",
                "-e", "XDG_SESSION_TYPE=X11",
+               "-e", "Q_MTLS_CA",
+               "-e", "Q_MTLS_CERT",
+               "-e", "Q_MTLS_KEY",
                "--device", "/dev/dri",
                "--security-opt=no-new-privileges:true",
                "--cap-drop=ALL",
@@ -477,9 +504,10 @@
        }
        if profile.HostAccess.Gpus != "" {
                if strings.HasSuffix(bin, "podman") {
-                       dockerArgs = append(dockerArgs, 
"--runtime=nvidia.com/gpu=all")
+                       dockerArgs = append(dockerArgs, 
"--device=nvidia.com/gpu=all")
+               } else {
+                       dockerArgs = append(dockerArgs, "--gpus", 
profile.HostAccess.Gpus)
                }
-               dockerArgs = append(dockerArgs, "--gpus", 
profile.HostAccess.Gpus)
        }
 
        if profile.DNS != "" {
@@ -536,6 +564,9 @@
 
        slog.Debug("exec: "+bin, "args", dockerArgs)
        cmd := execabs.Command(bin, dockerArgs...)
+       cmd.Env = append(os.Environ(), "Q_MTLS_CA="+string(ca))
+       cmd.Env = append(cmd.Env, "Q_MTLS_CERT="+string(cert))
+       cmd.Env = append(cmd.Env, "Q_MTLS_KEY="+string(key))
 
        output, err := cmd.CombinedOutput()
        if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/qubesome/run.go 
new/qubesome-0.0.8/internal/qubesome/run.go
--- old/qubesome-0.0.7/internal/qubesome/run.go 2024-11-25 11:55:53.000000000 
+0100
+++ new/qubesome-0.0.8/internal/qubesome/run.go 2024-12-08 10:40:21.000000000 
+0100
@@ -12,8 +12,6 @@
 
        securejoin "github.com/cyphar/filepath-securejoin"
        "github.com/qubesome/cli/internal/command"
-       "github.com/qubesome/cli/internal/drive"
-       "github.com/qubesome/cli/internal/env"
        "github.com/qubesome/cli/internal/files"
        "github.com/qubesome/cli/internal/images"
        "github.com/qubesome/cli/internal/inception"
@@ -22,6 +20,8 @@
        "github.com/qubesome/cli/internal/runners/podman"
        "github.com/qubesome/cli/internal/types"
        "github.com/qubesome/cli/internal/util/dbus"
+       "github.com/qubesome/cli/internal/util/drive"
+       "github.com/qubesome/cli/internal/util/env"
        "gopkg.in/yaml.v3"
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/runners/docker/run.go 
new/qubesome-0.0.8/internal/runners/docker/run.go
--- old/qubesome-0.0.7/internal/runners/docker/run.go   2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/runners/docker/run.go   2024-12-08 
10:40:21.000000000 +0100
@@ -9,13 +9,13 @@
        "strconv"
        "strings"
 
-       "github.com/qubesome/cli/internal/env"
        "github.com/qubesome/cli/internal/files"
        "github.com/qubesome/cli/internal/runners/util/container"
        "github.com/qubesome/cli/internal/runners/util/mime"
        "github.com/qubesome/cli/internal/runners/util/usb"
        "github.com/qubesome/cli/internal/types"
        "github.com/qubesome/cli/internal/util/dbus"
+       "github.com/qubesome/cli/internal/util/env"
        "github.com/qubesome/cli/internal/util/gpu"
        "golang.org/x/sys/execabs"
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/runners/podman/run.go 
new/qubesome-0.0.8/internal/runners/podman/run.go
--- old/qubesome-0.0.7/internal/runners/podman/run.go   2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/runners/podman/run.go   2024-12-08 
10:40:21.000000000 +0100
@@ -9,13 +9,13 @@
        "strconv"
        "strings"
 
-       "github.com/qubesome/cli/internal/env"
        "github.com/qubesome/cli/internal/files"
        "github.com/qubesome/cli/internal/runners/util/container"
        "github.com/qubesome/cli/internal/runners/util/mime"
        "github.com/qubesome/cli/internal/runners/util/usb"
        "github.com/qubesome/cli/internal/types"
        "github.com/qubesome/cli/internal/util/dbus"
+       "github.com/qubesome/cli/internal/util/env"
        "github.com/qubesome/cli/internal/util/gpu"
        "golang.org/x/sys/execabs"
 )
@@ -82,7 +82,7 @@
        }
 
        if wl.HostAccess.Gpus != "" {
-               args = append(args, "--gpus", wl.HostAccess.Gpus)
+               args = append(args, "--device=nvidia.com/gpu=all")
        }
 
        for _, cap := range wl.HostAccess.CapsAdd {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/socket/socket.go 
new/qubesome-0.0.8/internal/socket/socket.go
--- old/qubesome-0.0.7/internal/socket/socket.go        2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/socket/socket.go        1970-01-01 
01:00:00.000000000 +0100
@@ -1,80 +0,0 @@
-package socket
-
-import (
-       "fmt"
-       "log/slog"
-       "net"
-       "os"
-       "os/signal"
-       "path/filepath"
-       "syscall"
-
-       "github.com/qubesome/cli/internal/files"
-       "github.com/qubesome/cli/internal/types"
-)
-
-type ConnectionHandler func(cfg *types.Config, p *types.Profile, conn net.Conn)
-
-func Listen(p *types.Profile, cfg *types.Config, handler ConnectionHandler) 
error {
-       fn, err := files.SocketPath(p.Name)
-       if err != nil {
-               return err
-       }
-
-       // Removes previous versions of the socket that were not cleaned up.
-       if _, err := os.Stat(fn); err == nil {
-               _ = os.Remove(fn)
-       }
-
-       err = os.MkdirAll(filepath.Dir(fn), files.DirMode)
-       if err != nil {
-               return err
-       }
-
-       socket, err := net.Listen("unix", fn)
-       if err != nil {
-               return fmt.Errorf("failed to listen to socket: %w", err)
-       }
-       defer func() {
-               _ = os.Remove(fn)
-       }()
-
-       uid := os.Getuid()
-
-       err = os.Chmod(fn, files.FileMode)
-       if err != nil {
-               return err
-       }
-
-       pdir := fmt.Sprintf("/run/user/%d/qubesome/%s", uid, p.Name)
-       err = os.MkdirAll(pdir, files.DirMode)
-       if err != nil {
-               return fmt.Errorf("failed to create profile dir: %w", err)
-       }
-
-       err = os.Chmod(pdir, files.DirMode)
-       if err != nil {
-               return err
-       }
-
-       // Remove the sock file if the process is terminated.
-       c := make(chan os.Signal, 1)
-       signal.Notify(c, os.Interrupt, syscall.SIGTERM)
-       go func() {
-               <-c
-               os.Exit(1)
-       }()
-
-       slog.Debug("listening profile socket", "path", fn)
-       for {
-               // Accept an incoming connection.
-               conn, err := socket.Accept()
-               if err != nil {
-                       slog.Error("cannot accept connection", "error", err)
-                       continue
-               }
-
-               // Handle the connection in a separate goroutine.
-               go handler(cfg, p, conn)
-       }
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/types/workload.go 
new/qubesome-0.0.8/internal/types/workload.go
--- old/qubesome-0.0.7/internal/types/workload.go       2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/types/workload.go       2024-12-08 
10:40:21.000000000 +0100
@@ -6,7 +6,7 @@
        "slices"
        "strings"
 
-       "github.com/qubesome/cli/internal/env"
+       "github.com/qubesome/cli/internal/util/env"
 )
 
 type Workload struct {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/util/drive/drive.go 
new/qubesome-0.0.8/internal/util/drive/drive.go
--- old/qubesome-0.0.7/internal/util/drive/drive.go     1970-01-01 
01:00:00.000000000 +0100
+++ new/qubesome-0.0.8/internal/util/drive/drive.go     2024-12-08 
10:40:21.000000000 +0100
@@ -0,0 +1,47 @@
+package drive
+
+import (
+       "bufio"
+       "bytes"
+       "fmt"
+       "os"
+       "strings"
+)
+
+const mountsFile = "/proc/mounts"
+
+var readFile = os.ReadFile
+
+func Mounts(drive, mount string) (bool, error) {
+       if drive == "" {
+               return false, fmt.Errorf("drive is empty")
+       }
+       if mount == "" {
+               return false, fmt.Errorf("mount is empty")
+       }
+
+       data, err := readFile(mountsFile)
+       if err != nil {
+               return false, fmt.Errorf("failed to open mounts file: %w", err)
+       }
+
+       r := bytes.NewReader(data)
+       scanner := bufio.NewScanner(r)
+
+       for scanner.Scan() {
+               fields := strings.Fields(scanner.Text())
+               if fields[0] != drive {
+                       continue
+               }
+
+               if len(fields) <= 1 {
+                       continue
+               }
+
+               if fields[1] == mount {
+                       return true, nil
+               }
+       }
+
+       return false, nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/util/drive/drive_test.go 
new/qubesome-0.0.8/internal/util/drive/drive_test.go
--- old/qubesome-0.0.7/internal/util/drive/drive_test.go        1970-01-01 
01:00:00.000000000 +0100
+++ new/qubesome-0.0.8/internal/util/drive/drive_test.go        2024-12-08 
10:40:21.000000000 +0100
@@ -0,0 +1,89 @@
+package drive
+
+import (
+       "os"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestMounts(t *testing.T) {
+       tests := []struct {
+               name         string
+               drive        string
+               mount        string
+               want         bool
+               readFileFunc func(name string) ([]byte, error)
+               err          string
+       }{
+               {
+                       name:  "empty drive",
+                       mount: "/media",
+                       err:   "drive is empty",
+               },
+               {
+                       name:  "empty mount",
+                       drive: "/media",
+                       err:   "mount is empty",
+               },
+               {
+                       name:  "drive not found",
+                       drive: "foo-bar",
+                       mount: "/media",
+                       readFileFunc: func(name string) ([]byte, error) {
+                               return []byte("foo\nbar\n"), nil
+                       },
+               },
+               {
+                       name:  "mounts file not found",
+                       drive: "foo-bar",
+                       mount: "/media",
+                       readFileFunc: func(name string) ([]byte, error) {
+                               return nil, os.ErrNotExist
+                       },
+                       err: "failed to open mounts file: file does not exist",
+               },
+               {
+                       name:  "single mount point",
+                       drive: "foo-bar",
+                       mount: "/media/foo/bar",
+                       readFileFunc: func(name string) ([]byte, error) {
+                               return []byte("foo\nbar\nfoo-bar 
/media/foo/bar\n"), nil
+                       },
+                       want: true,
+               },
+               {
+                       name:  "multiple mount points",
+                       drive: "foo-bar",
+                       mount: "/media/foo/bar",
+                       readFileFunc: func(name string) ([]byte, error) {
+                               return []byte("foo-bar 
/foo/for/bar\nfoo\nbar\nfoo-bar /media/foo/bar\n"), nil
+                       },
+                       want: true,
+               },
+               {
+                       name:  "not right mount point",
+                       drive: "foo-bar",
+                       mount: "/media/bar/foo",
+                       readFileFunc: func(name string) ([]byte, error) {
+                               return []byte("foo\nbar\nfoo-bar 
/media/foo/bar\n"), nil
+                       },
+                       want: false,
+               },
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.name, func(t *testing.T) {
+                       readFile = tc.readFileFunc
+                       got, err := Mounts(tc.drive, tc.mount)
+
+                       if tc.err == "" {
+                               require.NoError(t, err)
+                               assert.Equal(t, tc.want, got)
+                       } else {
+                               require.ErrorContains(t, err, tc.err)
+                       }
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/util/env/env.go 
new/qubesome-0.0.8/internal/util/env/env.go
--- old/qubesome-0.0.7/internal/util/env/env.go 1970-01-01 01:00:00.000000000 
+0100
+++ new/qubesome-0.0.8/internal/util/env/env.go 2024-12-08 10:40:21.000000000 
+0100
@@ -0,0 +1,41 @@
+package env
+
+import (
+       "fmt"
+       "log/slog"
+       "os"
+)
+
+func init() { //nolint
+       h, _ := os.UserHomeDir()
+       _ = Update("HOME", h)
+}
+
+var mapping = map[string]string{
+       "HOME":   "",
+       "GITDIR": "",
+}
+
+func Update(k, v string) error {
+       slog.Debug("setting env", k, v)
+       if _, ok := mapping[k]; ok {
+               mapping[k] = v
+               return nil
+       }
+       return fmt.Errorf("%q is not an expandable env var", k)
+}
+
+func Add(k, v string) {
+       mapping[k] = v
+}
+
+func Expand(in string) string {
+       return os.Expand(in, expand)
+}
+
+func expand(s string) string {
+       if out, ok := mapping[s]; ok {
+               return out
+       }
+       return ""
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/util/mtls/mtls.go 
new/qubesome-0.0.8/internal/util/mtls/mtls.go
--- old/qubesome-0.0.7/internal/util/mtls/mtls.go       1970-01-01 
01:00:00.000000000 +0100
+++ new/qubesome-0.0.8/internal/util/mtls/mtls.go       2024-12-08 
10:40:21.000000000 +0100
@@ -0,0 +1,159 @@
+package mtls
+
+import (
+       "bytes"
+       "crypto/ecdsa"
+       "crypto/elliptic"
+       "crypto/rand"
+       "crypto/tls"
+       "crypto/x509"
+       "crypto/x509/pkix"
+       "encoding/pem"
+       "fmt"
+       "math/big"
+       "time"
+)
+
+const (
+       validFor = 7 * 24 * time.Hour // 7 days
+       // ProfileServerName sets the server name for the qubesome profile.
+       ProfileServerName = "qubesome-profile"
+       // HostServerName sets the server name for the qubesome host.
+       HostServerName = "qubesome-host"
+)
+
+type Credentials struct {
+       ServerCert   tls.Certificate
+       CA           []byte
+       ClientPEM    []byte
+       ClientKeyPEM []byte
+}
+
+func NewCredentials() (*Credentials, error) {
+       caCert, caKey, caBytes, err := generateCA()
+       if err != nil {
+               return nil, err
+       }
+
+       serverCertBytes, serverKey, err := generateCert(caCert, caKey, true)
+       if err != nil {
+               return nil, err
+       }
+       serverCertPEM, serverKeyPEM, err := pemEncode(serverCertBytes, 
serverKey)
+       if err != nil {
+               return nil, err
+       }
+
+       serverCert, err := tls.X509KeyPair(serverCertPEM, serverKeyPEM)
+       if err != nil {
+               return nil, err
+       }
+
+       clientCertBytes, clientKey, err := generateCert(caCert, caKey, false)
+       if err != nil {
+               return nil, err
+       }
+       clientCertPEM, clientKeyPEM, err := pemEncode(clientCertBytes, 
clientKey)
+       if err != nil {
+               return nil, err
+       }
+
+       ca := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: 
caBytes})
+       return &Credentials{
+               ServerCert:   serverCert,
+               CA:           ca,
+               ClientPEM:    clientCertPEM,
+               ClientKeyPEM: clientKeyPEM,
+       }, nil
+}
+
+// generateCA generates an in-memory CA certificate and private key.
+func generateCA() (*x509.Certificate, *ecdsa.PrivateKey, []byte, error) {
+       priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
+       if err != nil {
+               return nil, nil, nil, fmt.Errorf("failed to generate CA private 
key: %w", err)
+       }
+
+       template := &x509.Certificate{
+               SerialNumber: big.NewInt(time.Now().UnixNano()),
+               Subject: pkix.Name{
+                       CommonName:   "qubesome inception CA",
+                       Organization: []string{"qubesome"},
+               },
+               NotBefore:             time.Now(),
+               NotAfter:              time.Now().Add(validFor),
+               KeyUsage:              x509.KeyUsageCertSign | 
x509.KeyUsageCRLSign,
+               ExtKeyUsage:           
[]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
+               IsCA:                  true,
+               BasicConstraintsValid: true,
+               SignatureAlgorithm:    x509.ECDSAWithSHA256,
+       }
+
+       certBytes, err := x509.CreateCertificate(rand.Reader, template, 
template, &priv.PublicKey, priv)
+       if err != nil {
+               return nil, nil, nil, fmt.Errorf("failed to create CA 
certificate: %w", err)
+       }
+
+       certPEM := new(bytes.Buffer)
+       err = pem.Encode(certPEM, &pem.Block{
+               Type:  "CERTIFICATE",
+               Bytes: certBytes,
+       })
+       if err != nil {
+               return nil, nil, nil, fmt.Errorf("failed to PEM encode 
certificate: %w", err)
+       }
+
+       cert, err := x509.ParseCertificate(certBytes)
+       if err != nil {
+               return nil, nil, nil, fmt.Errorf("failed to parse CA 
certificate: %w", err)
+       }
+
+       return cert, priv, certBytes, nil
+}
+
+// generateCert generates a certificate signed by caCert.
+func generateCert(caCert *x509.Certificate, caKey *ecdsa.PrivateKey, isServer 
bool) ([]byte, *ecdsa.PrivateKey, error) {
+       priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
+       if err != nil {
+               return nil, nil, fmt.Errorf("failed to generate private key: 
%w", err)
+       }
+
+       template := &x509.Certificate{
+               SerialNumber: big.NewInt(time.Now().UnixNano()),
+               Subject: pkix.Name{
+                       Organization: []string{"qubesome"},
+               },
+               NotBefore:          time.Now(),
+               NotAfter:           time.Now().Add(validFor),
+               KeyUsage:           x509.KeyUsageDigitalSignature | 
x509.KeyUsageKeyEncipherment,
+               ExtKeyUsage:        
[]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
+               SignatureAlgorithm: x509.ECDSAWithSHA256,
+       }
+
+       if isServer {
+               template.ExtKeyUsage = 
[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
+               template.DNSNames = []string{HostServerName}
+       } else {
+               template.DNSNames = []string{ProfileServerName}
+       }
+
+       certBytes, err := x509.CreateCertificate(rand.Reader, template, caCert, 
&priv.PublicKey, caKey)
+       if err != nil {
+               return nil, nil, fmt.Errorf("failed to create certificate: %w", 
err)
+       }
+
+       return certBytes, priv, nil
+}
+
+// pemEncode encodes the certificate and private key to PEM format.
+func pemEncode(certBytes []byte, priv *ecdsa.PrivateKey) ([]byte, []byte, 
error) {
+       certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: 
certBytes})
+
+       privBytes, err := x509.MarshalECPrivateKey(priv)
+       if err != nil {
+               return nil, nil, fmt.Errorf("failed to marshal private key: 
%w", err)
+       }
+       keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: 
privBytes})
+
+       return certPEM, keyPEM, nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/util/xauth/xauth.go 
new/qubesome-0.0.8/internal/util/xauth/xauth.go
--- old/qubesome-0.0.7/internal/util/xauth/xauth.go     2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/util/xauth/xauth.go     2024-12-08 
10:40:21.000000000 +0100
@@ -16,13 +16,13 @@
 var cookieFunc = newCookie
 
 func AuthPair(display uint8, parent io.Reader, server, client io.Writer) error 
{
-       data := make([]byte, 50)
+       data := make([]byte, 40)
        n, err := parent.Read(data)
        if err != nil {
                return fmt.Errorf("failed to read from parent auth file: %w", 
err)
        }
-       if n < 50 {
-               return fmt.Errorf("auth file must be at least 50 chars long: 
was %d instead", n)
+       if n < 40 {
+               return fmt.Errorf("auth file must be at least 40 chars long: 
was %d instead", n)
        }
 
        _, _ = server.Write(data[0:2])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/internal/util/xauth/xauth_test.go 
new/qubesome-0.0.8/internal/util/xauth/xauth_test.go
--- old/qubesome-0.0.7/internal/util/xauth/xauth_test.go        2024-11-25 
11:55:53.000000000 +0100
+++ new/qubesome-0.0.8/internal/util/xauth/xauth_test.go        2024-12-08 
10:40:21.000000000 +0100
@@ -105,3 +105,16 @@
                })
        }
 }
+
+func FuzzToNumberic(f *testing.F) {
+       f.Fuzz(func(t *testing.T, input []byte) {
+               ToNumeric(input)
+       })
+}
+
+func FuzzAuthPair(f *testing.F) {
+       f.Fuzz(func(t *testing.T, display uint8, parent []byte) {
+               _ = AuthPair(display, bytes.NewReader(parent),
+                       &bytes.Buffer{}, &bytes.Buffer{})
+       })
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/qubesome-0.0.7/pkg/inception/server.go 
new/qubesome-0.0.8/pkg/inception/server.go
--- old/qubesome-0.0.7/pkg/inception/server.go  2024-11-25 11:55:53.000000000 
+0100
+++ new/qubesome-0.0.8/pkg/inception/server.go  2024-12-08 10:40:21.000000000 
+0100
@@ -2,6 +2,8 @@
 
 import (
        "context"
+       "crypto/tls"
+       "crypto/x509"
        "fmt"
        "log/slog"
        "net"
@@ -10,8 +12,10 @@
        "github.com/qubesome/cli/internal/command"
        "github.com/qubesome/cli/internal/qubesome"
        "github.com/qubesome/cli/internal/types"
+       "github.com/qubesome/cli/internal/util/mtls"
        pb "github.com/qubesome/cli/pkg/inception/proto"
        "google.golang.org/grpc"
+       "google.golang.org/grpc/credentials"
 )
 
 // NewServer returns a new inception server.
@@ -32,13 +36,26 @@
        server *grpcServer
 }
 
-func (s *Server) Listen(socket string) error {
+func (s *Server) Listen(serverCert tls.Certificate, ca []byte, socket string) 
error {
        lis, err := net.Listen("unix", socket)
        if err != nil {
                return fmt.Errorf("failed to listen: %w", err)
        }
 
-       gs := grpc.NewServer()
+       certPool := x509.NewCertPool()
+       if !certPool.AppendCertsFromPEM(ca) {
+               return fmt.Errorf("failed to append CA from PEM")
+       }
+
+       creds := credentials.NewTLS(&tls.Config{
+               Certificates: []tls.Certificate{serverCert},
+               ClientAuth:   tls.RequireAndVerifyClientCert,
+               ClientCAs:    certPool,
+               MinVersion:   tls.VersionTLS13,
+               ServerName:   mtls.ProfileServerName,
+       })
+
+       gs := grpc.NewServer(grpc.Creds(creds))
        pb.RegisterQubesomeHostServer(gs, s.server)
 
        slog.Debug("[server] listening", "addr", lis.Addr())

++++++ vendor.tar.gz ++++++
++++ 5233 lines of diff (skipped)

Reply via email to